Using Array objects as key for ES6 Map
Solution 1
Understand that ES2015 Map keys are compared (almost) as if with the ===
operator. Two array instances, even if they contain the same values, do not ever compare as ===
to each other.
Try this:
var a = new Map(), key = ['x', 'y'];
a.set(key, 1);
console.log(a.get(key));
Since the Map class is intended to be usable as a base class, you could implement a subclass with an overriding .get()
function, maybe.
(The "almost" in the first sentence is to reflect the fact that Map key equality comparison is done via Object.is()
, which doesn't come up much in daily coding. It's essentially a third variation on equality testing in JavaScript.)
Solution 2
I also had this need, so I wrote an ISC-licensed library: array-keyed-map. You can find it on npm. I think the source is quite clear, but here's how it works anyway, for posterity:
Maintain a tree of Map
objects. Each tree stores:
Under an internally-declared
Symbol
key: The value at that point in the tree (if any). TheSymbol
guarantees uniqueness, so no user-provided value can overwrite this key.On all its other keys: all other so-far set next-trees from this tree.
For example, on akmap.set(['a', 'b'], true)
, the internal tree structure would be like—
'a':
[value]: undefined
'b':
[value]: true
Doing akmap.set(['a'], 'okay')
after that would just change the value for the path at 'a'
:
'a':
[value]: 'okay'
'b':
[value]: true
To get the value for an array, iterate through the array while reading the corresponding keys off the tree. Return undefined
if the tree at any point is non-existent. Finally, read the internally declared [value]
symbol off the tree you've gotten to.
To delete a value for an array, do the same but delete any values under the [value]
-symbol-key, and delete any child trees after the recursive step if they ended up with a size
of 0.
Why a tree? Because it's very efficient when multiple arrays have the same prefixes, which is pretty typical in real-world use, for working with e.g. file paths.
Solution 3
You need to save a reference to the non-primitive instance of Array
you used as a key. Notice the difference in the following two examples:
"use strict";
var a = new Map();
a.set(['x','y'], 1);
console.log(a.get(['x','y']));
console.log(['x','y'] === ['x','y']);
var b = new Map();
var array = ['x','y'];
b.set(array, 1);
console.log(b.get(array));
console.log(array === array);
Solution 4
In case someone is looking here for a more basic approach, you can apply a custom 1-to-1 conversion of keys to strings, like in ${x}_${y}
(relying on a specific structure of keys) or JSON.stringify(key)
(more generic) - which by no means is a perfect solution, yet it can be most reliable in many cases.
I know the OP asked for other approaches, but I think this one still needs to be mentioned here as the most direct answer to the title question.
jimjampez
Updated on June 13, 2022Comments
-
jimjampez almost 2 years
I am trying to update my code to ES6 as I am using Node 4.0 and really like its features so far. However I have problems with the new ES6
Map
data structure as it behaves differently to{}
when usingArray
as a key. I am using it as a counter map.I run this code and I would like to know how I can use arrays as keys for the
Map
."use strict"; var a = new Map(); a.set(['x','y'], 1); console.log(a.get(['x','y'])); var b = {}; b[['x','y']] = 1; console.log(b[['x','y']]);
It prints out the following and the first line should be
1
and notundefined
:undefined 1
The original JS map stringifies the key and I am not wanting to do the same type of stringify hack with the new ES6
Map
.What can I do to use arrays as keys reliably for a ES6
Map
?