Comparing version 0.1.0 to 0.2.0
{ | ||
"name": "stringset", | ||
"version": "0.1.0", | ||
"version": "0.2.0", | ||
"description": "fast and robust stringset", | ||
@@ -5,0 +5,0 @@ "main": "stringset.js", |
@@ -12,3 +12,3 @@ # stringset.js | ||
```javascript | ||
var StringSet = require("./stringset"); | ||
var StringSet = require("stringset"); | ||
@@ -49,3 +49,3 @@ var ss1 = new StringSet(); | ||
### Browser | ||
Clone the repo, include it in a script tag. You may want an ES5 polyfill. | ||
Clone the repo and include it in a script tag | ||
@@ -52,0 +52,0 @@ git clone https://github.com/olov/stringset.git |
174
stringset.js
@@ -5,31 +5,143 @@ // stringset.js | ||
// A stringset that handles problematic items just fine | ||
// For now "__proto__" is the only item that is handled specially | ||
// Other problematic items, if available in a JS VM near you (let me know), | ||
// can easily be moved to the aux object | ||
var StringSet = (function() { | ||
"use strict"; | ||
var token = Object.create(null); | ||
// to save us a few characters | ||
var hasOwnProperty = Object.prototype.hasOwnProperty; | ||
var create = (function() { | ||
function hasOwnEnumerableProps(obj) { | ||
for (var prop in obj) { | ||
if (hasOwnProperty.call(obj, prop)) { | ||
return true; | ||
} | ||
} | ||
return false; | ||
} | ||
// FF <= 3.6: | ||
// o = {}; o.hasOwnProperty("__proto__" or "__count__" or "__parent__") => true | ||
// o = {"__proto__": null}; Object.prototype.hasOwnProperty.call(o, "__proto__" or "__count__" or "__parent__") => false | ||
function hasOwnPollutedProps(obj) { | ||
return hasOwnProperty.call(obj, "__count__") || hasOwnProperty.call(obj, "__parent__"); | ||
} | ||
var useObjectCreate = false; | ||
if (typeof Object.create === "function") { | ||
if (!hasOwnEnumerableProps(Object.create(null))) { | ||
useObjectCreate = true; | ||
} | ||
} | ||
if (useObjectCreate === false) { | ||
if (hasOwnEnumerableProps({})) { | ||
throw new Error("StringSet environment error 0, please file a bug at https://github.com/olov/stringset/issues"); | ||
} | ||
} | ||
// no throw yet means we can create objects without own enumerable props (safe-guard against VMs and shims) | ||
var o = (useObjectCreate ? Object.create(null) : {}); | ||
var useProtoClear = false; | ||
if (hasOwnPollutedProps(o)) { | ||
o.__proto__ = null; | ||
if (hasOwnEnumerableProps(o) || hasOwnPollutedProps(o)) { | ||
throw new Error("StringSet environment error 1, please file a bug at https://github.com/olov/stringset/issues"); | ||
} | ||
useProtoClear = true; | ||
} | ||
// no throw yet means we can create objects without own polluted props (safe-guard against VMs and shims) | ||
return function() { | ||
var o = (useObjectCreate ? Object.create(null) : {}); | ||
if (useProtoClear) { | ||
o.__proto__ = null; | ||
} | ||
return o; | ||
}; | ||
})(); | ||
// stringset ctor | ||
function stringset(optional_array) { | ||
// use with or without new | ||
if (!(this instanceof stringset)) { | ||
return new stringset(optional_array); | ||
} | ||
this.obj = Object.create(null); | ||
this.aux = Object.create(null); | ||
this.obj = create(); | ||
this.hasProto = false; // false (no __proto__ item) or true (has __proto__ item) | ||
if (optional_array) { | ||
this.addMany(optional_array); | ||
} | ||
} | ||
}; | ||
// primitive methods that deals with data representation | ||
stringset.prototype.has = function(item) { | ||
return (item === "__proto__" ? "proto" in this.aux : item in this.obj); | ||
// The type-check of item in has, get, set and delete is important because otherwise an object | ||
// {toString: function() { return "__proto__"; }} can avoid the item === "__proto__" test. | ||
// The alternative to type-checking would be to force string conversion, i.e. item = String(item); | ||
if (typeof item !== "string") { | ||
throw new Error("StringSet expected string item"); | ||
} | ||
return (item === "__proto__" ? | ||
this.hasProto : | ||
hasOwnProperty.call(this.obj, item)); | ||
}; | ||
stringset.prototype.add = function(item) { | ||
if (typeof item !== "string") { | ||
throw new Error("StringSet expected string item"); | ||
} | ||
if (item === "__proto__") { | ||
this.aux.proto = token; | ||
this.hasProto = true; | ||
} else { | ||
this.obj[item] = token; | ||
this.obj[item] = true; | ||
} | ||
}; | ||
stringset.prototype['delete'] = function(item) { | ||
if (typeof item !== "string") { | ||
throw new Error("StringSet expected string item"); | ||
} | ||
var didExist = this.has(item); | ||
if (item === "__proto__") { | ||
this.hasProto = false; | ||
} else { | ||
delete this.obj[item]; | ||
} | ||
return didExist; | ||
}; | ||
stringset.prototype.isEmpty = function() { | ||
for (var item in this.obj) { | ||
if (hasOwnProperty.call(this.obj, item)) { | ||
return false; | ||
} | ||
} | ||
return !this.hasProto; | ||
}; | ||
stringset.prototype.size = function() { | ||
var len = 0; | ||
for (var item in this.obj) { | ||
if (hasOwnProperty.call(this.obj, item)) { | ||
++len; | ||
} | ||
} | ||
return (this.hasProto ? len + 1 : len); | ||
}; | ||
stringset.prototype.items = function() { | ||
var items = []; | ||
for (var item in this.obj) { | ||
if (hasOwnProperty.call(this.obj, item)) { | ||
items.push(item); | ||
} | ||
} | ||
if (this.hasProto) { | ||
items.push("__proto__"); | ||
} | ||
return items; | ||
}; | ||
// methods that rely on the above primitives | ||
stringset.prototype.addMany = function(items) { | ||
@@ -44,22 +156,8 @@ if (!Array.isArray(items)) { | ||
}; | ||
stringset.prototype.merge = function(set) { | ||
this.addMany(set.items()); | ||
stringset.prototype.merge = function(other) { | ||
this.addMany(other.items()); | ||
return this; | ||
}; | ||
stringset.prototype['delete'] = function(item) { | ||
var existed = this.has(item); | ||
if (item === "__proto__") { | ||
delete this.aux.proto; | ||
} else { | ||
delete this.obj[item]; | ||
} | ||
return existed; | ||
}; | ||
stringset.prototype.items = function() { | ||
var items = Object.keys(this.obj); | ||
if (this.aux.proto) { | ||
items.push("__proto__"); | ||
} | ||
return items; | ||
}; | ||
stringset.prototype.clone = function() { | ||
@@ -69,20 +167,8 @@ var other = stringset(); | ||
}; | ||
stringset.prototype.isEmpty = function() { | ||
for (var item in this.obj) { | ||
return false; | ||
} | ||
return !("proto" in this.aux); | ||
}; | ||
stringset.prototype.size = function() { | ||
var len = 0; | ||
for (var item in this.obj) { | ||
++len; | ||
} | ||
return ("proto" in this.aux ? len + 1 : len); | ||
}; | ||
stringset.prototype.toString = function() { | ||
return "{" + this.items().map(JSON.stringify).join(",") + "}"; | ||
} | ||
}; | ||
return stringset; | ||
})(); | ||
@@ -89,0 +175,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
8799
170