0

JavaScript ES6 introduced Map implemented with hash table. Since hash table look up time is on average O(1), for random accessed data, Map seems to be a good choice for data storage.

However, JavaScript does not have data structures like struct in C++ that can be used as keys in the Map to enable "multiple keys mapping". The closest ones are Objects, but their instances do not equal to each other even if "contents are the same".

If I want to save a 2D or 3D tile based game map using the Map type, is there a way to easily access the blocks given the coordinates? Of course strings like "1,2,3" (representing x,y,z) would work, but is there a way that we can use integers as keys?

And if I must fall back to using assembled string coordinates, would the performance decrease a lot?

EDIT: I want to use a hash table because there may be "holes" in the maps and tiles may be created randomly in the middle of nowhere.

Daniel Cheung
  • 4,258
  • 1
  • 25
  • 54
  • 1
    What is expected data structure? `[[1,2,3], ['value']]`? – guest271314 Feb 02 '19 at 07:17
  • @guest271314 I want to store tile objects in the world in a map like `{[1,2,3]: Object}`. They would contain tile information and geometries that I would want to get rendered by something like Three.js. – Daniel Cheung Feb 02 '19 at 07:19
  • Not looking good for customizing the equality relation https://stackoverflow.com/questions/29759480/how-to-customize-object-equality-for-javascript-set – glumplum Feb 02 '19 at 07:23
  • Regarding your edit, How would a Map help? If the key is not set, you'll face the same with a Map, an Array or any Object. – Kaiido Feb 02 '19 at 08:02
  • @Kaiido I believe hash tables allow the creation of a key out of random, but Arrays would fill the void with null. Wouldn't that create wasted space? – Daniel Cheung Feb 02 '19 at 08:07
  • No. `const a = []; a[42] = 'the answer';` will create a [sparse Array](https://books.google.co.kr/books?id=6TAODdEIxrgC&pg=PA144&lpg=PA144&dq=sparse+array+definitive), its `length` property will be set to `43` but `a[0~41]` are untouched and definitely not the value `null`. Sparse Arrays may make some optimizations inefective, mostly when accessing from a loop, but since it seems you only do random-access, these optimizations would probably not kick in anyway, so you are just fine with an Array, even if all the slots are not being used. – Kaiido Feb 02 '19 at 08:13
  • @Kaiido You're right. I've just created https://jsperf.com/sparse-array-objects-and-maps/ and indeed, on FF Array and Object are the same and fastest. On Chrome, Array is the fastest and same as to FF. – Daniel Cheung Feb 02 '19 at 08:30
  • @Kalido In my opinion a sparse array indicates a type error and should be avoided. –  Feb 02 '19 at 08:33
  • @reify glad you're not a js-engine then. (while your nickname would be a funny name for one) – Kaiido Feb 02 '19 at 10:05

4 Answers4

1

What you are asking for is just a multidimensional Array. If you are going to use only integer keys, there is absolutely no benefit to use a Map.

const map = [];
for(let x=0; x<2; x++) {
  let xArr = [];
  map.push(xArr);
  for(let y=0; y<2; y++) {
    let yArr = [];
    xArr.push(yArr);
    for(let z=0; z<2; z++) {
      yArr.push(`${x},${y},${z}`);
    }
  }
}
console.log(map[1][1][0]);
console.log(map);
Kaiido
  • 87,051
  • 7
  • 143
  • 194
1

I just made a performance test between storing in Array, and Nested Object as well as Object with string keys. The result is surprising to me. The fastest is Object with string keys.

https://jsperf.com/multiple-dimension-sparse-matrix

Array           OPS 0.48    ±5.19%     77% slower    //a[z][y][x]
Nested Object   OPS 0.51    ±16.65%    77% slower    //a[z][y][x]
String Object   OPS 2.96    ±29.77%    fastest       //a["x,y,z"]
Daniel Cheung
  • 4,258
  • 1
  • 25
  • 54
1

I changed Daniels' performance test by adding the "Left shifting" case, where we left shift those numbers (as long as we don't extrapolate the number limits). I got the following results:

Array           OPS 0.90    ±4.68%     91% slower    //a[z][y][x]
Nested Object   OPS 0.86    ±3.25%     92% slower    //a[z][y][x]
String Object   OPS 3.59    ±16.90%    68% slower    //a["x,y,z"]
Left Shift      OPS 10.68   ±11.69%    fastest       //a[(x<<N1)+(y<<N2)+z]
Anderson
  • 31
  • 1
  • 1
  • 5
0

Other than joining the keys into a string like you mentioned (which is perfectly fine IMO), another option is to use multiple nested Maps. For example, for a 3d structure, accessing 1,2,3 (axes x, y, and z, let's say) could be done with

bigMap.get(1).get(2).get(3)

where the bigMap (containing the 3d structure) contains Map values for each y and z slice, and each of those has Map keys corresponding to each z slice, and each z-slice Map has values containing the information at each coordinate.

But while this is a possibility, your idea of an object or Map indexed by comma-joined coordinates is perfectly fine and probably more understandable at a glance IMO.

Remember that object property lookup is O(1) just like Map lookup - there's no performance hit if you use a single object (or Map) indexed by strings.

CertainPerformance
  • 260,466
  • 31
  • 181
  • 209
  • I don't know where I got the impression that objects aren't necessarily implemented with hash tables and may have been linked list or some other data types? But if the only difference of objects and Maps are whether the keys can be objects, then this works. It has been tested that string keys are much slower than number keys. I know it's not going to affect small projects, but I'm just wondering what's the fasted way to implement something like this. – Daniel Cheung Feb 02 '19 at 07:42
  • Would a 3D array be a better alternative? `array3D[1][2][3] = obj`? – glumplum Feb 02 '19 at 07:49
  • Sure, that works too. There are multiple fine options for this sort of situation – CertainPerformance Feb 02 '19 at 07:50
  • Well OP is concerned about says that Maps with string keys are slow and i wonder (without bothering to experiment and find out) if arrays (which are just objects) end up being faster with integer keys. – glumplum Feb 02 '19 at 07:58
  • @glumplum Arrays use Strings too. But given how Arrays are optimized, I'd still bet on them if I saw them in a contests against Maps. – Kaiido Feb 02 '19 at 08:00