Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Typescript/Javascript: using tuple as key of Map

Hit this odd bug in my code and I can't figure the way to get a constant time lookup from a Map when using a tuple as my key.

Hopefully this illustrates the issue, and the workaround I'm using now just to get it to work:

hello.ts:

let map: Map<[number, number], number> = new Map<[number, number], number>()
    .set([0, 0], 48);

console.log(map.get([0,0])); // prints undefined

console.log(map.get(String([0, 0]))); //  compiler:  error TS2345: Argument of type 
// 'string' is not assignable to parameter of type '[number, number]'.

//the work-around:
map.forEach((value: number, key: [number, number]) => {
    if(String(key) === String([0, 0])){
        console.log(value); // prints 48
    }
})

To compile (transpile?) I'm using:

tsc hello.ts -target es6

tsc version 2.1.6

Tried several things to make the Map.get() method to work, not having much success.

like image 430
ZackDeRose Avatar asked Apr 24 '17 15:04

ZackDeRose


1 Answers

In JavaScript (and as an extension, TypeScript), no two arrays are equal except if they refer to the same array (i.e., when changing the elements of one also would change the elements of another). If you create a new array with the same elements, it would not consider it to be equal to any existing one.

Because Maps consider such equality when looking up elements, if you store a value with an array as a key, you can only get the value out again if you pass in the exact same array reference as a key again:

const map: Map<[ number, number], number> = new Map<[ number, number ], number>();

const a: [ number, number ] = [ 0, 0 ];
const b: [ number, number ] = [ 0, 0 ];

// a and b have the same value, but refer to different arrays so are not equal
a === b; // = false

map.set(a, 123);
map.get(a); // = 123
map.get(b); // = undefined

One simple workaround for this is to use strings or numbers as keys, as these are always considered equal when they have the same value:

const map: Map<string, number> = new Map<string, number>();

const a: [ number, number ] = [ 0, 0 ];
const b: [ number, number ] = [ 0, 0 ];

const astr: string = a.join(','); // = '0,0'
const bstr: string = b.join(','); // = '0,0'

// astr and bstr have the same value, and are strings so they are always equal
astr === bstr; // = true

map.set(astr, 123);
map.get(astr); // = 123
map.get(bstr); // = 123
like image 198
Frxstrem Avatar answered Sep 16 '22 21:09

Frxstrem