Comparing version 0.1.1 to 0.2.0
{ | ||
"name": "stringmap", | ||
"version": "0.1.1", | ||
"version": "0.2.0", | ||
"description": "fast and robust stringmap", | ||
@@ -5,0 +5,0 @@ "main": "stringmap.js", |
@@ -62,3 +62,3 @@ # stringmap.js | ||
### 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 | ||
@@ -65,0 +65,0 @@ git clone https://github.com/olov/stringmap.git |
235
stringmap.js
@@ -5,15 +5,67 @@ // stringmap.js | ||
// A stringmap that handles problematic keys just fine | ||
// For now "__proto__" is the only key that is handled specially | ||
// Other problematic keys, if available in a JS VM near you (let me know), | ||
// can easily be moved to the aux object | ||
var StringMap = (function() { | ||
"use strict"; | ||
// 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("StringMap environment error 0, please file a bug at https://github.com/olov/stringmap/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("StringMap environment error 1, please file a bug at https://github.com/olov/stringmap/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; | ||
}; | ||
})(); | ||
// stringmap ctor | ||
function stringmap(optional_object) { | ||
// use with or without new | ||
if (!(this instanceof stringmap)) { | ||
return new stringmap(optional_object); | ||
} | ||
this.obj = Object.create(null); | ||
this.aux = Object.create(null); | ||
this.obj = create(); | ||
this.hasProto = undefined; // undefined (no __proto__ key) or true (has __proto__ key), never false | ||
this.proto = undefined; // value for __proto__ key when hasProto is true, undefined otherwise | ||
if (optional_object) { | ||
@@ -23,11 +75,32 @@ this.setMany(optional_object); | ||
} | ||
// primitive methods that deals with data representation | ||
stringmap.prototype.has = function(key) { | ||
// The type-check of key in has, get, set and delete is important because otherwise an object | ||
// {toString: function() { return "__proto__"; }} can avoid the key === "__proto__" test. | ||
// The alternative to type-checking would be to force string conversion, i.e. key = String(key); | ||
if (typeof key !== "string") { | ||
throw new Error("StringMap expected string key"); | ||
} | ||
return (key === "__proto__" ? | ||
this.hasProto : | ||
hasOwnProperty.call(this.obj, key)); | ||
}; | ||
stringmap.prototype.get = function(key) { | ||
return (key === "__proto__" ? this.aux.proto : this.obj[key]); | ||
if (typeof key !== "string") { | ||
throw new Error("StringMap expected string key"); | ||
} | ||
return (key === "__proto__" ? | ||
this.hasProto && this.proto : | ||
(hasOwnProperty.call(this.obj, key) ? this.obj[key] : undefined)); | ||
}; | ||
stringmap.prototype.has = function(key) { | ||
return (key === "__proto__" ? "proto" in this.aux : key in this.obj); | ||
}; | ||
stringmap.prototype.set = function(key, value) { | ||
if (typeof key !== "string") { | ||
throw new Error("StringMap expected string key"); | ||
} | ||
if (key === "__proto__") { | ||
this.aux.proto = value; | ||
this.hasProto = true; | ||
this.proto = value; | ||
} else { | ||
@@ -37,33 +110,43 @@ this.obj[key] = value; | ||
}; | ||
stringmap.prototype.setMany = function(object) { | ||
if (object === null || (typeof object !== "object" && typeof object !== "function")) { | ||
throw new Error("StringMap expected Object"); | ||
stringmap.prototype['delete'] = function(key) { | ||
if (typeof key !== "string") { | ||
throw new Error("StringMap expected string key"); | ||
} | ||
var keys = Object.keys(object); | ||
for (var idx = 0; idx < keys.length; idx++) { | ||
var key = keys[idx]; | ||
this.set(key, object[key]); | ||
var didExist = this.has(key); | ||
if (key === "__proto__") { | ||
this.hasProto = this.proto = undefined; | ||
} else { | ||
delete this.obj[key]; | ||
} | ||
return this; | ||
return didExist; | ||
}; | ||
stringmap.prototype.merge = function(map) { | ||
var keys = map.keys(); | ||
for (var idx = 0; idx < keys.length; idx++) { | ||
var key = keys[idx]; | ||
this.set(key, map.get(key)); | ||
stringmap.prototype.isEmpty = function() { | ||
for (var key in this.obj) { | ||
if (hasOwnProperty.call(this.obj, key)) { | ||
return false; | ||
} | ||
} | ||
return this; | ||
return !this.hasProto; | ||
}; | ||
stringmap.prototype['delete'] = function(key) { | ||
var existed = this.has(key); | ||
if (key === "__proto__") { | ||
delete this.aux.proto; | ||
} else { | ||
delete this.obj[key]; | ||
stringmap.prototype.size = function() { | ||
var len = 0; | ||
for (var key in this.obj) { | ||
if (hasOwnProperty.call(this.obj, key)) { | ||
++len; | ||
} | ||
} | ||
return existed; | ||
return (this.hasProto ? len + 1 : len); | ||
}; | ||
stringmap.prototype.keys = function() { | ||
var keys = Object.keys(this.obj); | ||
if (this.aux.proto) { | ||
var keys = []; | ||
for (var key in this.obj) { | ||
if (hasOwnProperty.call(this.obj, key)) { | ||
keys.push(key); | ||
} | ||
} | ||
if (this.hasProto) { | ||
keys.push("__proto__"); | ||
@@ -73,29 +156,69 @@ } | ||
}; | ||
stringmap.prototype.values = function() { | ||
var arr = this.keys(); | ||
for (var idx = 0; idx < arr.length; idx++) { | ||
arr[idx] = this.get(arr[idx]); | ||
var values = []; | ||
for (var key in this.obj) { | ||
if (hasOwnProperty.call(this.obj, key)) { | ||
values.push(this.obj[key]); | ||
} | ||
} | ||
return arr; | ||
if (this.hasProto) { | ||
values.push(this.proto); | ||
} | ||
return values; | ||
}; | ||
stringmap.prototype.items = function() { | ||
var arr = this.keys(); | ||
for (var idx = 0; idx < arr.length; idx++) { | ||
arr[idx] = [arr[idx], this.get(arr[idx])]; | ||
var items = []; | ||
for (var key in this.obj) { | ||
if (hasOwnProperty.call(this.obj, key)) { | ||
items.push([key, this.obj[key]]); | ||
} | ||
} | ||
return arr; | ||
if (this.hasProto) { | ||
items.push(["__proto__", this.proto]); | ||
} | ||
return items; | ||
}; | ||
// methods that rely on the above primitives | ||
stringmap.prototype.setMany = function(object) { | ||
if (object === null || (typeof object !== "object" && typeof object !== "function")) { | ||
throw new Error("StringMap expected Object"); | ||
} | ||
for (var key in object) { | ||
if (hasOwnProperty.call(object, key)) { | ||
this.set(key, object[key]); | ||
} | ||
} | ||
return this; | ||
}; | ||
stringmap.prototype.merge = function(other) { | ||
var keys = other.keys(); | ||
for (var i = 0; i < keys.length; i++) { | ||
var key = keys[i]; | ||
this.set(key, other.get(key)); | ||
} | ||
return this; | ||
}; | ||
stringmap.prototype.map = function(fn) { | ||
var arr = this.keys(); | ||
for (var idx = 0; idx < arr.length; idx++) { | ||
arr[idx] = fn(this.get(arr[idx]), arr[idx]); | ||
var keys = this.keys(); | ||
for (var i = 0; i < keys.length; i++) { | ||
var key = keys[i]; | ||
keys[i] = fn(this.get(key), key); // re-use keys array for results | ||
} | ||
return arr; | ||
return keys; | ||
}; | ||
stringmap.prototype.forEach = function(fn) { | ||
var arr = this.keys(); | ||
for (var idx = 0; idx < arr.length; idx++) { | ||
fn(this.get(arr[idx]), arr[idx]); | ||
var keys = this.keys(); | ||
for (var i = 0; i < keys.length; i++) { | ||
var key = keys[i]; | ||
fn(this.get(key), key); | ||
} | ||
}; | ||
stringmap.prototype.clone = function() { | ||
@@ -105,15 +228,3 @@ var other = stringmap(); | ||
}; | ||
stringmap.prototype.isEmpty = function() { | ||
for (var key in this.obj) { | ||
return false; | ||
} | ||
return !("proto" in this.aux); | ||
}; | ||
stringmap.prototype.size = function() { | ||
var len = 0; | ||
for (var key in this.obj) { | ||
++len; | ||
} | ||
return ("proto" in this.aux ? len + 1 : len); | ||
}; | ||
stringmap.prototype.toString = function() { | ||
@@ -120,0 +231,0 @@ var self = this; |
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
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
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
11501
242
1