29

I have a simple example on python:

programs = {}
if not programs.has_key(( program, time )):
     programs[( program, time )] = 0
programs[( program, time )] = programs[( program, time )] + 1

How to use array as key in Javascript ?

Bdfy
  • 23,141
  • 55
  • 131
  • 179
  • 2
    Do you mean you want to use an array as a lookup in a hash? – alex Apr 16 '12 at 12:22
  • 1
    Depending on the the possible values of `program` and `time`, it might not be a problem to just use the string representation of `[program, time]` as key. Since the value you use as property is automatically converted to a string, usage is straightforward. – Felix Kling Apr 16 '12 at 12:26
  • like Rob said,custom the key like **"program_time"**,and handle it by yourself – tym1193 Apr 16 '12 at 12:27
  • 2
    Possible duplicate of [Using Array objects as key for ES6 Map](https://stackoverflow.com/questions/32660188/using-array-objects-as-key-for-es6-map) – Anko - inactive in protest Apr 08 '19 at 07:37

4 Answers4

47

This will "work". (but I don't recommend it)

var a = {};
var b = [1,2,3];    
a[b] = 'hello';

// a[b] evaluates to 'hello'
// a[[1,2,3]] evaluates to 'hello'
// a['1,2,3'] evaluates to 'hello'

It works because when you pass the array [1,2,3] as the hash (map/associative-array) key, is being converted to the string '1,2,3' before performing the hash lookup. It should suit your needs as long as you don't need two different arrays of the same value to map to different hash values.

var c = [1,2,3]
// a[c] evaluates to 'hello' even though we never executed a[c] = 'hello'
// but b == c evaluates to false
// b & c are two separate objects with the same values, so when they
// get converted to a string for hashing, they return the same value from the hash

As it was mentioned, you'll need more than the standard JavaScript hash if you want to use object references as your keys.

Update

Based on the comment from @speedplane:

I suspect that JS calls toString() on the array when you pass it into a hash key. So you can easily test what you're actually going to get as your key:

 ["x", "y", "z"].toString();                // 'x,y,z'
 ["x,y,z"].toString();                    // 'x,y,z'
 [1,2,3].toString();                      // '1,2,3'
 [1,2,'3'].toString();                    // '1,2,3'
 [[1],[2],[3]].toString();                // '1,2,3'
 [["x",1], ["y",2], ["z",3]].toString();  // 'x,1,y,2,z,3'

So again, I recommend that you don't do this unless you really understand what is going on. And even then, I wouldn't do it.

geisterfurz007
  • 5,292
  • 5
  • 33
  • 54
Walter Stabosz
  • 7,447
  • 5
  • 43
  • 75
  • 4
    Note that this does not work so well if you're using an array of strings as the key. For example, `var a={}; var b = ["x", "y", "z"]; var c = ["x,y,z"]; a[b] = 'uh oh'; a[c]` will evaluate to `uh oh`. The two different arrays `b` and `c` map to the same object key. – speedplane Sep 08 '16 at 10:15
  • @speedplane Thanks for pointing that out. I updated my answer with your examples. Also, I clearly stated that I wouldn't recommend that you do this. – Walter Stabosz Sep 08 '16 at 20:31
  • I am surprised by the popularity of this answer. – Walter Stabosz May 12 '21 at 17:47
  • 1
    You can confirm that Javascript does indeed call toString by overwriting the toString method: `let x = [1, 2, 3]; x.toString = ()=>{console.log("Called!")}; let obj = {}; obj[x] = 1;` – leumasme Aug 01 '21 at 10:59
  • Cool idea for how to verify the call to toString. From an academical interest, I wonder if there is a way to make the redefined `x.toString` still return a string, so that the key of `obj[x]` is not `undefined` but rather `'1,2,3'` .... the only way I could figure it out was to create a copy of x: `x.toString = ()=>{ let t = [...x]; console.log("Called!"); return t.toString() }` – Walter Stabosz Aug 01 '21 at 14:51
22

JavaScript keys are strings.

You need a WeakMap, or a custom method to map arrays to other objects.

Rob W
  • 341,306
  • 83
  • 791
  • 678
  • 9
    map.set([1,2], 'woo'); map.get([1,2]) -> undefined. Is there a way to use an array in that way? – chris Feb 02 '15 at 22:38
  • 9
    `[1, 2]` and `[1, 2]` are different objects. Use `var arr = [1, 2];` `map.set(arr, 'woo');` and `map.get(arr, 'woo');`. If you cannot retain a reference to the array, then you have to serialize/hash it in some way. Naively, e.g. using `arr.join(',')` as a key. – Rob W Feb 02 '15 at 22:40
  • Yeah I ended up hashing it in some weird way :) I didn't want to use strings because it's happening maybe millions of times at once. Too bad - the arrays would be nice. – chris Feb 03 '15 at 15:09
8

I've written a library called array-keyed-map for doing this robustly in modern JavaScript. Unlike the other answers so far posted, it does not rely on serialising values into strings, but instead uses ES2015 Map objects, which can accept arbitrary values as keys.

I'll quote my answer to a different question for an implementation overview, so the method is preserved posterity in case the library disappears for some reason, or you want to implement it yourself:

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). The Symbol 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.

  • +1 for tree structure, this incorporates the essence of the "trie" data structure, which is used in all kinds of algorithms. [More](https://www.hackerearth.com/practice/notes/trie-suffix-tree-suffix-array/) [info](https://en.wikipedia.org/wiki/Trie). – Exr0n Apr 14 '20 at 19:20
-2

Will this do the trick for you?

jsfiddle

<script>
var ary = {person1:'valerie', person2:'alex'};
for (key in ary) {
    document.write(key, '<br>')
}

document.write(ary['person2'], '<br>')
</script>
Ryan Fiorini
  • 800
  • 5
  • 9