Creating Unique Key/Value Pairs with Uniquely Different Keys
Problem
You want to create a set of key/value pairs, but not all of the keys are strings.
Solution
Use the new Map object:
var myMap = new Map(); myMap.set("value1", "this is value"); myMap.set(3, "another value"); myMap.set(NaN, "isn't this interesting"); myMap.set(window, "really now"); console.log(myMap.get("value1")); // this is a value console.log(myMap.get(NaN)); // isn't this interesting console.log(myMap.get(3)); // another value console.log(myMap.get(window)); // really now
EXPLAIN
Prior to ES 6, you could create key/value pairs by creating an Object using a specialized
syntax (to avoid default values), and adding each key and value to the new object:
var newObj = Object.create(null, { prop1: { value: undefined, enumerable: true, writable: true, configurable: true }, prop2: { value: undefined, enumerable: true, writable: true, configurable: true } }); newObj.prop1 = "first"; newObj.prop2 = "second"; newObj.prop3 = "third"; // added third prop console.log(newObj.prop1); // first console.log(newObj.prop2); // second
Using Object.create() with null as the first parameter ensures that the object doesn’t inherit the Object prototype, and the only properties are those specifically added. If you didn’t care about creating a completely clean object, you could also use an object literal:
var newObj = { prop1: "first", prop2: "second"};
One limitation with using objects as a way of mapping key/value pairs, other than having to ensure they’re created without an existing prototype, is that keys are strings. Even when it seems as if our use of non-string values is acceptable:
newObj.NaN = 'hey, it works!';
What happens is the NaN is really converted into a string representation of NaN:
for (var prop in newObj) {
console.log(prop); // NaN prints out as "NaN" }
In addition, trying to assign a property such as a function, or a number, throws an error:
newObj.5 = 'no way'; newObj.function() {} = 'Seriously?';
However, functions, numbers, strings, objects…all of these are allowable with a Map, as demonstrated in the solution. Use set() to add new key/value pairs, and delete() to remove those no longer needed:
myMap.set('mykey',100); myMap.delete('mykey');
And use clear() to clear all collection members. Unlike object properties, it’s simple to discover how many key/value pairs a Map contains:
console.log(myMap.size); // 3
And you can easily traverse through both keys and values:
// iterating through both key an value for (var [key, value] of myMap) { console.log(key + " = " + value); } // iterating through keys for (var key of myMap.keys()) { console.log(key); } // iterating through values for (var value of myMap.values()) { console.log(value); } // using forEach myMap.forEach(function(value) { console.log(value); });
All the traversals use iterators, except for forEach(). The first example uses an implicit entries() method that returns an object with an array of [key, value] pairs, allowing us to access both in the loop. The entries(), keys(), and values() function all return iterators, which also means we can use the following syntax with each:
var mapIter = myMap.keys(); console.log(mapIter.next().value); // "value1" console.log(mapIter.next().value); // 3 console.log(mapIter.next().value); // NaN
You can also discover if a specific key is contained in a Map collection with has():
myMap.set(23,'value'); console.log(myMap.has(23)); // true
The solution demonstrates using NaN as a key value. As the Mozilla Developer Network entry on Map notes, while NaN is technically not equivalent to itself, when used as a key, it is equivalent:
myMap.set(NaN, 'nada'); var id = Number('not a number'); console.log(myMap.get(id)); // 'nada'
When all is said and done, though, you may want to just forgo using NaN as a key.
ADVANCE
As with Sets, Map keys and values can be objects, as well as scalar values. This includes
the built-in objects, such as window. But the objects must be exactly equal—they can’t
be equivalent. In the following code snippet, two object literals, an array, and window
are used to set members in the Map.
The code tries to access the Array object’s value
using another equivalent array literal, but it doesn’t work. Only when I use the exact
same object (whether through the original variable, or a new one assigned the object), can the application access the value:
var b = {first: 'apple', second: 'pear'}; var c = {first: '5', second: '1'}; var d = [1,2,3,4]; var e = b; var myMap = new Map(); myMap.set(b, 'first'); myMap.set(c, 'second'); myMap.set(d, 'array'); myMap.set(window,'global'); console.log(myMap.get(window)); // 'global' console.log(myMap.get([1,2,3,4])); // undefined console.log(myMap.get(d)); // 'array' console.log(myMap.get(e)); // 'first'
There is also another new object, WeakMap, that functions in a similar manner to Map, except the collection only accepts object keys, and the object keys are held weakly, and therefore can be garbage collected. This means the WeakMap collection can’t be enu‐ merated, but does help prevent unexpected and unwanted memory leaks.
No comments:
Post a Comment