Overriding the equality function of a Javascript Map

Viewed 1927

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?

2 Answers

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.

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]);

Related