Is it possible to add a new element to top of Map? [closed]

Tags:



Is it possible to add a new element to top of Map without sorting?

Like as unshift()?

Because Map is it like a object, I each time sort object after adding operation.

I have done this like that:

let map = new Map();

map.set(1, { id: 1 });
map.set(2, { id: 2 });
map.set(3, { id: 3 });
map.set(4, { id: 4 });

let mapCopy = new Map();


for (let i = map.size; i > 0 ; --i) {
    mapCopy.set(i, map.get(i));
}

console.log(map);
console.log(mapCopy);

Answer

A Map preserves the insertion order of keys by specification:

const map = new Map();

map.set(3, "gamma");
map.set(1, "alpha");
map.set(2, "beta");

print(map);

function print(map) {
  for ([key, value] of map.entries()) 
    console.log(`${key} - ${value}`)
}

If you want to move something to the end, it has to be the last inserted item. You can “move” by deleting and re-adding:

const map = new Map();

map.set(3, "gamma");
map.set(1, "alpha");
map.set(2, "beta");

moveToEnd(map, 3);

print(map);

function print(map) {
  for ([key, value] of map.entries()) 
    console.log(`${key} - ${value}`)
}

//modifies in-place
function moveToEnd(map, key) {
  //save value
  const value = map.get(key);
  //delete
  map.delete(key);
  //re-insert
  map.set(key, value);
}

Note that you must delete, otherwise it doesn’t work:

const map = new Map();

map.set(3, "gamma");
map.set(1, "alpha");
map.set(2, "beta");

moveToEnd(map, 3);

print(map);

function print(map) {
  for ([key, value] of map.entries()) 
    console.log(`${key} - ${value}`)
}

//modifies in-place
function moveToEnd(map, key) {
  //save value
  const value = map.get(key);
  
  //don't delete
  
  //re-insert
  map.set(key, value);
}

Another option is to re-create the entire Map and enforce the new insertion order:

const map = new Map();

map.set(3, "gamma");
map.set(1, "alpha");
map.set(2, "beta");

const newMap1 = moveToEnd1(map, 3);
const newMap2 = moveToEnd2(map, 3);

print(newMap1);
console.log("------")
print(newMap2);

function print(map) {
  for ([key, value] of map.entries()) 
    console.log(`${key} - ${value}`)
}

function moveToEnd1(map, key) {
  //create a new Map from the old one
  const result = new Map(map);
  
  //save value
  const value = map.get(key);
  //delete
  result.delete(key);
  //re-insert
  result.set(key, value);
  
  return result;
}

function moveToEnd2(map, key) {
  return [...map.entries()]                        //get all entries
    .filter(([k,]) => k !== key)                   //remove all but the key that would be last
    .reduce(                                       //create a new Map inserting all other entries
      (acc, [key, value]) => acc.set(key, value), 
      new Map()
    )
    .set(key, map.get(key));                       //add the last entry
}

However, a move to front means that you have to shift everything else to the front. Again, you can do the same as before – either move the entries in-place by deleting and re-adding the keys:

const map = new Map();

map.set(3, "gamma");
map.set(1, "alpha");
map.set(2, "beta");

const newMap1 = moveToFront(map, 1);

print(map);

function print(map) {
  for ([key, value] of map.entries()) 
    console.log(`${key} - ${value}`)
}

function moveToFront(map, key) {
  //materialise all entries, because the iterator provides a live view
  const entries = Array.from(map.entries());
  
  //move to the back
  for (let [k, v] of entries) {
    //skip moving the target key
    if (k === key) continue; 
    
    //delete
    map.delete(k);
    //re-insert
    map.set(k, v);
  }
}

Or re-create the map with the new order. Note that if you insert the desired key in front, you can just use set again with it and it won’t move, as long as there is no .delete() called for it, which makes the re-creation easier:

const map = new Map();

map.set(3, "gamma");
map.set(1, "alpha");
map.set(2, "beta");

const newMap = moveToFront(map, 1);
print(newMap);

function print(map) {
  for ([key, value] of map.entries()) 
    console.log(`${key} - ${value}`)
}

function moveToFront(map, key) {
  return new Map([
    [key, map.get(key)], //key-value to be in front
    ...map               //merge with the entire map
  ]);
}

As for actually adding instead of moving – the same applies, you can either shift everything in the map or just re-create it. Assuming you want to treat a repeat inset as “move to front”, then you can do something like this:

const map = new Map();

map.set(3, "gamma");
map.set(1, "alpha");
map.set(2, "beta");

addToFrontInPlace(map, 4, "delta");
print(map);
console.log("-------");
addToFrontInPlace(map, 1, "new alpha");
print(map);

function print(map) {
  for ([key, value] of map.entries()) 
    console.log(`${key} - ${value}`)
}

function addToFrontInPlace(map, key, value) {
  //add new value
  map.set(key, value);
  
  //materialise all entries, because the iterator provides a live view
  const entries = Array.from(map.entries());
  
  //move to the back
  for (let [k, v] of entries) {
    //skip moving the target key
    if (k === key) continue; 
    
    //delete
    map.delete(k);
    //re-insert
    map.set(k, v);
  }

}

const map = new Map();

map.set(3, "gamma");
map.set(1, "alpha");
map.set(2, "beta");

const newMap1 = addToFrontNewMap(map, 4, "delta");
print(newMap1);
console.log("-------");
const newMap2 = addToFrontNewMap(newMap1, 1, "new alpha");
print(newMap2);

function print(map) {
  for ([key, value] of map.entries()) 
    console.log(`${key} - ${value}`)
}

function addToFrontNewMap(map, key, value = 7) {
  //exclude the entry from the old map, so it doesn't overwrite the value
  const entries = [...map.entries()]
      .filter(([k,]) => k !== key);
      
  return new Map([
    [key, value], //key-value to be in front
    ...entries    //merge with the entire map
  ]);
}


Source: stackoverflow