1

I have a need for a list type object simultaneously providing the contrary JS array behaviours of being iterable and associative. So at one point I want to run an index based walk through the members and in another I want to do 'does this key exist' and 'update the data under that key'.

I see many question in SO regarding discussions about associative arrays but this question differs because I am seeking re-usable code that wraps both behaviours. The solution can be a modifier to the array prototype, or a stand-alone object creation function. I accept that the list must be unique by key, and FYI in my case the members will be objects themselves.

Please note I know that there is no such thing as an associative array in JavaScript - I don't need that sermon. I seek the a solution for a single object with both capabilities on the same list.

Here is a rudimentary version in a working snippet. I am looking for a more fleshed out version that includes the full set of life-cycle features such as disposal, etc..

var List = function() {
  this.elements={} // assoc list of elements.
  this.elementArray=[] // plain array
  this.addElement=function (id, member) {
    this.elements[id]=member;
    this.elementArray.push(member);
    }
}

var myList = new List();

myList.addElement('V', {id: 'key2', name: 'Volvo'});
myList.addElement('A', {id: 'key4', name: 'Apple'});
myList.addElement('S', {id: 'key3', name: 'Google'});

console.log('Via iteration');
for (var i = 0; i < myList.elementArray.length; i = i + 1) {
  console.log('id=' + myList.elementArray[i].id + ' name=' + myList.elementArray[i].name); 
}

console.log('Via association');
console.log('Value of A: id=' + myList.elements['A'].id + ' name=' + myList.elements['A'].name);
console.log('Value of V: id=' + myList.elements['V'].id + ' name=' + myList.elements['V'].name);
console.log('Value of S: id=' + myList.elements['S'].id + ' name=' + myList.elements['S'].name);
Vanquished Wombat
  • 9,075
  • 5
  • 28
  • 67
  • 1
    The built-in [`Map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) seems to fit your requirements, as it provides both direct (`.get`, `.has`) and sequential (`.entries`) access. – georg Jun 08 '17 at 08:50
  • Can you provide a link to docs? – Vanquished Wombat Jun 08 '17 at 08:51
  • 1
    Edited above ^^^^^ – georg Jun 08 '17 at 08:52
  • @georg Map looks to be what I need except the Browser Compatibility table on the [linked page](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) show some gaps for IE11. Is Map considered viable cross browsers? – Vanquished Wombat Jun 08 '17 at 10:05
  • 1
    For older browsers there are polyfills, see e.g. https://stackoverflow.com/questions/34789810/how-does-babel-implement-set-map-polyfills – georg Jun 08 '17 at 10:33
  • @georg thanks for that link. The linked babeljs file is illuminating educationally. Whilst Map / Set looks like a perfect solution for the future, I will see what other pure JS suggestions (no polyfills) come in. I have nothing specific against polyfills - I just want a pure solution if there is one. On the upside, the fact that Map / Set is required affirms that there is no out-of-the-tin capability in current cross-browser JS. – Vanquished Wombat Jun 08 '17 at 12:19

1 Answers1

2

I know this is far away from done, but it might give you a starting point, extending the Array class gives you the array's special length property (adds on push, when its modified it modifies the array itself, etc)

'use strict';

class AssociativeArray extends Array {
   constructor() {
      super();
      this.data = {};
   }
   push(key, value) {
      super.push(key);
      this.data[key] = value
   }
   *[Symbol.iterator]() {
      let nextIndex = 0;
      while(nextIndex < this.length) {
        yield this[nextIndex];
        nextIndex++;
      }
   }
}

var myAssociativeArray = new AssociativeArray();
myAssociativeArray.push("1st", "Bamieh");
myAssociativeArray.push("2nd", "value");
myAssociativeArray.push("3nd", "anotherValue");
console.log('myAssociativeArray::', myAssociativeArray);
console.log('myAssociativeArray.length::', myAssociativeArray.length);

for (let key of myAssociativeArray) {
  console.log('key::', key);
}

basically you are reimplementing the Map or the Set functions, which are natively supported in es6. its a good practice non-the-less.

Bamieh
  • 10,358
  • 4
  • 31
  • 52