4

I want to create a Map (dictionary) in Javascript where they key is an array of numbers, and the value is some object. I want to retrieve from the map a value previously stored using the same array contents - i.e. if I have an array with some contents I want to retrieve the value (if any) previously stored for an array with the same content.

In most other languages this would be easy. You would override the equality function of the Map so that two arrays with the same content tested equal. This doesn't seem to be available Javascript. Map keys are testing using SameValueZero, which only considers two objects equal if they are the same object.

Is there a way of overriding equality, or is there another standard way of handling this case in Javascript?

DJClayworth
  • 26,349
  • 9
  • 53
  • 79
  • 1
    `.prototype` might be what you are looking for. – Ryan Wilson Oct 29 '18 at 18:04
  • @RyanWilson Would you care to elaborate? – DJClayworth Oct 29 '18 at 18:06
  • You can probably do this by redefining `Array.prototype.map`, but I'd suggest taking a look at a library like underscore or lodash, which have some good functionality in this regard. – Jpec07 Oct 29 '18 at 18:07
  • 1
    @DJClayworth Please see the 3rd answer on this post (https://stackoverflow.com/questions/10539938/override-the-equivalence-comparison-in-javascript) by user CBusBus, this shows how to use prototype, there are some other options available in that post as well which may give you what you want. – Ryan Wilson Oct 29 '18 at 18:07

2 Answers2

1

You can use a Proxy to change how properties of objects (which also means elements of arrays) are accessed. They let you intercept property gets and sets with a callback.

Here's an example of a proxied array that stores only two elements, and returns one of those two elements regardless of what index you ask for.

    const arr = new Proxy([], {
      get: function(self, prop) {
        const index = parseInt(prop, 10)
        // If the property is not a number, then no special handling
        if (isNaN(index)) {
          return self[prop]
        }
        
        if (index % 2 === 0) {
          // even
          return self[0]
        } else {
          // odd
          return self[1]
        }
      }
    })
    
    // Add just two elements
    arr.push('even')
    arr.push('odd')
    
    // Get any index
    console.log(arr[0]) // even
    console.log(arr[1]) // odd
    console.log(arr[2]) // even
    console.log(arr[301]) // odd

Alternatively, you could write your own class that doesn't rely on the "magic" of intercepting basic operators and just have get(key) and set(key, value) methods that use whatever logic you want.

Alex Wayne
  • 178,991
  • 47
  • 309
  • 337
0

If you don't need the special features of a Map (built-in iteration over values in insertion order), and your desired equality function could work by comparing the toString() values of the arrays, you can use a standard object:

const notReallyAMap = {};
notReallyAMap[[0, 1, 2]] = 'whole';
notReallyAMap[[0.5, 2.75, 6.125]] = 'rational';
notReallyAMap[[1, 2, 3, 4, 5]] = 'my luggage';

const testArray = [1, 2, 3, 4, 5];
console.log(notReallyAMap[testArray]);
AuxTaco
  • 4,883
  • 1
  • 12
  • 27