Insert at specific index in a Map

Viewed 4928

As the documentation for Map says:

The keys in Map are ordered while keys added to object are not. Thus, when iterating over it, a Map object returns keys in order of insertion.

To take full advantage of this, I need to insert an element into a Map at a given specific order.

I know this could be done with an array, but I prefer to use a Map because I need to do a lot of lookups by key, and a Map works well for that case, while being easy to maintain.

I came up with something like this and I would like to know if there are any better ways to do it.

  function insertAtIndex(index, key, value, map){
    var iterator1 = map[Symbol.iterator]();
    var tmpMap = new Map();
    var tmpIndex=0;
    for (let item of iterator1) {
        if(tmpIndex === index){
            tmpMap.set(key, value);
        }
        tmpMap.set(item[0], item[1]);
        tmpIndex++;
    }
    return tmpMap;
  }

or

  insertCardAtIndex(index: Number, key: string, value:boardCard, map:Map<string, boardCard>): Map<string, boardCard> {
    let clonedMap = new Map(map);
    let tmpMap = new Map<string, boardCard>();
    let tmpIndex = 0;

    for (let entry of Array.from(clonedMap.entries())) {
      if(tmpIndex === index){
        tmpMap.set(key, value);
      }
      tmpMap.set(entry[0], entry[1]);
      tmpIndex++;
    }

    return tmpMap;
  }

For my use case, I will never have to deal with a map that is big enough to make performance a main concern, so I am willing to sacrifice performance if that makes the code more readable and maintainable.

I am using map has a "dictionary" of values by . The fact that there is no way to insert an element in a particular order makes me wonder if I should use an array instead, even though having quick lookups, and methods like .has(), .get(), and .set() that are really useful but not worth the trouble of not being able to insert at a certain index.

1 Answers

You can convert the Map to an Array, use Array.splice to insert the element, and then convert to a Map again. This is not an efficient solution, but you mentioned maintainability is more important than performance in your use case.

This version has the advantage of working if your index is greater than the size of the map. For example inserting an element at position map.size + 1 will still add the item at the bottom, where in your algorithm the item would not be inserted. If you do want to ignore insertions that are out of bounds that should be easily addressable with an check in the function but this seemed more robust.

function insertAtIndex(index, key, value, map){
  const arr = Array.from(map);
  arr.splice(index, 0, [key, value]);
  return new Map(arr);
}

const m = new Map();
m.set('0', 0);
m.set('1', 1);
m.set('2', 2);

console.log(Array.from(m.keys()));

let m2 = insertAtIndex(1, '0.5', 0.5, m);
console.log(Array.from(m2));
m2 = insertAtIndex(0, '-1', -1, m2);
m2 = insertAtIndex(5, '5', 5, m2);
m2 = insertAtIndex(10, '10', 10, m2);

console.log(Array.from(m2));

Note that, similar to the functions in the question itself, the above function creates and returns a new Map, which means old references to the map are invalidated after inserting. This is ok if you assume immutability, but if your code is generally mutable and you may assume references to the map need to stay valid you need to do something like the following:

 function insertAtIndex(index, key, value, map){
  const arr = Array.from(map);
  arr.splice(index, 0, [key, value]);
  map.clear();
  arr.forEach(([k,v]) => map.set(k,v));
}

const m = new Map();
m.set('0', 0);
m.set('1', 1);
m.set('2', 2);

console.log(Array.from(m.keys()));

insertAtIndex(1, '0.5', 0.5, m);
insertAtIndex(0, '-1', -1, m);
insertAtIndex(5, '5', 5, m);
insertAtIndex(10, '10', 10, m);

console.log(Array.from(m));

Related