Too low rep, can't add comment so I'm adding it as an answer here.
In advance, apologies for the wall of text.
I like CTS_AE's answer, and wanted to take the same route.
However, there is one thing to note, and that is how are arrays put to String.
let memory = {};
memory[[1,2,3]] = "123";
console.log(memory[[1,2,3]]); // "123"
console.log([1,2,3].toString(); // "1,2,3"
console.log(memory["1,2,3"]); // "123"
Now this wouldn't be such an issue if you knew what you were putting in are exclusively arrays... or would it?
MDN about Array.prototype.toString() says
For Array objects, the toString method joins the array and returns one string containing each array element separated by commas.
Which brings 2 big issues:
- indexing via
array
is the same as indexing via array.toSring()
- since
toString()
is recursive, nested arrays end up being stringified into the same format as single-level arrays.
let memory = {};
memory[[1,2,3]] = "123";
console.log(memory[[1,2,3]]); // "123"
console.log(memory["1,2,3"]); // "123"
console.log(memory[[[1],[2],[3]]]); // "123"
console.log(memory[[[[1],2],3]]); // "123"
...and the list goes on. It's not hard to see when these issues can really break your project. I encountered such issue just a while ago when I tried memoizing
function doSomeStuff(string, sourceIdxs, targetIdxs) {
if (memo[[string, sourceIdxs, targetIdxs]])
return memo[[string, sourceIdxs, targetIdxs]];
// ...
}
In such case for example ["foo", [1, 3, 5], [6, 10]]
and ["foo", [1, 3], [5, 6, 10]]
point to the same value, and I ended up overwriting existing values, effectively corrupting the function memory.
Now, in the specific case of the ArraySet
answer above, the issue persists. While you don't mind if you overwrite existing key with another "same" key, you can end up getting false positives.
So how to fix this?
Option 1. stringify
The easy way out is using JSON.stringify()
to write the "exact" string representations of all the key data.
let memory = {};
memory[JSON.stringify([1,2,3])] = "123";
console.log(memory[JSON.stringify([1,2,3])]); // "123"
console.log(memory[JSON.stringify([[1],[2,3]])); // undefined
This helps with the false positives.... kind of. For one, you won't have issues with overlapping elements in the array. [[1,2],[3]]
no longer points to where [[1],[2,3]]
does.
However, [1,2,3]
and "[1,2,3]"
do. Also (in some bizarre cases), the array elements might contain [
or ]
characters, which might complicate the issue further.
And you might not care for such cases. Your keys may still be restrained well enough for such things not to happen. And you might even want such behaviour. If you do, go for it.
Good:
- Fixes most of issues of the
ArraySet
- Is easy to implement
Bad:
JSON.stringify()
is fairly slow.
Option 2. separators
Much simpler and quicker, while still giving a bit of advantage over the old solution.
function toSepString(arr) { return arr.join("|") }
let memory = {};
memory[toSepString([[1,2],3])] = "123";
console.log(memory[toSepString([1,[2,3]])]); // undefined
Now of course this helps only with the "outer-most" layer.
console.log(toSepString([1,[2,[3]]])); // "1|2,3"
console.log(toSepString([1,[[2],[[3]]]]); // "1|2,3"
So should you use this, you need to make sure the specific elements of your key array can't become ambiguous when converted to string.
Of course, you could make the function recursive and add "[", "]"
on each beginning and end, essentially copying a part of the functionality of JSON.stringify()
. I would imagine this would be still more performant than calling JSON.stringify()
directly, but that would require tests.
Good:
- faster than its built-in counterpart
Bad:
- issues with nested arrays
- issues with separator not appearing in values