[javascript] .map() a Javascript ES6 Map?

How would you do this? Instinctively, I want to do:

var myMap = new Map([["thing1", 1], ["thing2", 2], ["thing3", 3]]);

// wishful, ignorant thinking
var newMap = myMap.map((key, value) => value + 1); // Map { 'thing1' => 2, 'thing2' => 3, 'thing3' => 4 }

I've haven't gleaned much from the documentation on the new iteration protocol.

I am aware of wu.js, but I'm running a Babel project and don't want to include Traceur, which it seems like it currently depends on.

I also am a bit clueless as to how to extract how fitzgen/wu.js did it into my own project.

Would love a clear, concise explanation of what I'm missing here. Thanks!


Docs for ES6 Map, FYI

This question is related to javascript ecmascript-6

The answer is


If you don't want to convert the entire Map into an array beforehand, and/or destructure key-value arrays, you can use this silly function:

_x000D_
_x000D_
/**_x000D_
 * Map over an ES6 Map._x000D_
 *_x000D_
 * @param {Map} map_x000D_
 * @param {Function} cb Callback. Receives two arguments: key, value._x000D_
 * @returns {Array}_x000D_
 */_x000D_
function mapMap(map, cb) {_x000D_
  let out = new Array(map.size);_x000D_
  let i = 0;_x000D_
  map.forEach((val, key) => {_x000D_
    out[i++] = cb(key, val);_x000D_
  });_x000D_
  return out;_x000D_
}_x000D_
_x000D_
let map = new Map([_x000D_
  ["a", 1],_x000D_
  ["b", 2],_x000D_
  ["c", 3]_x000D_
]);_x000D_
_x000D_
console.log(_x000D_
  mapMap(map, (k, v) => `${k}-${v}`).join(', ')_x000D_
); // a-1, b-2, c-3
_x000D_
_x000D_
_x000D_


Using Array.from I wrote a Typescript function that maps the values:

function mapKeys<T, V, U>(m: Map<T, V>, fn: (this: void, v: V) => U): Map<T, U> {
  function transformPair([k, v]: [T, V]): [T, U] {
    return [k, fn(v)]
  }
  return new Map(Array.from(m.entries(), transformPair));
}

const m = new Map([[1, 2], [3, 4]]);
console.log(mapKeys(m, i => i + 1));
// Map { 1 => 3, 3 => 5 }

Maybe this way:

const m = new Map([["a", 1], ["b", 2], ["c", 3]]);
m.map((k, v) => [k, v * 2]); // Map { 'a' => 2, 'b' => 4, 'c' => 6 }

You would only need to monkey patch Map before:

Map.prototype.map = function(func){
    return new Map(Array.from(this, ([k, v]) => func(k, v)));
}

We could have wrote a simpler form of this patch:

Map.prototype.map = function(func){
    return new Map(Array.from(this, func));
}

But we would have forced us to then write m.map(([k, v]) => [k, v * 2]); which seems a bit more painful and ugly to me.

Mapping values only

We could also map values only, but I wouldn't advice going for that solution as it is too specific. Nevertheless it can be done and we would have the following API:

const m = new Map([["a", 1], ["b", 2], ["c", 3]]);
m.map(v => v * 2); // Map { 'a' => 2, 'b' => 4, 'c' => 6 }

Just like before patching this way:

Map.prototype.map = function(func){
    return new Map(Array.from(this, ([k, v]) => [k, func(v)]));
}

Maybe you can have both, naming the second mapValues to make it clear that you are not actually mapping the object as it would probably be expected.


You can use myMap.forEach, and in each loop, using map.set to change value.

_x000D_
_x000D_
myMap = new Map([_x000D_
  ["a", 1],_x000D_
  ["b", 2],_x000D_
  ["c", 3]_x000D_
]);_x000D_
_x000D_
for (var [key, value] of myMap.entries()) {_x000D_
  console.log(key + ' = ' + value);_x000D_
}_x000D_
_x000D_
_x000D_
myMap.forEach((value, key, map) => {_x000D_
  map.set(key, value+1)_x000D_
})_x000D_
_x000D_
for (var [key, value] of myMap.entries()) {_x000D_
  console.log(key + ' = ' + value);_x000D_
}
_x000D_
_x000D_
_x000D_


_x000D_
_x000D_
const mapMap = (callback, map) => new Map(Array.from(map).map(callback))_x000D_
_x000D_
var myMap = new Map([["thing1", 1], ["thing2", 2], ["thing3", 3]]);_x000D_
_x000D_
var newMap = mapMap((pair) => [pair[0], pair[1] + 1], myMap); // Map { 'thing1' => 2, 'thing2' => 3, 'thing3' => 4 }
_x000D_
_x000D_
_x000D_


Actually you can still have a Map with the original keys after converting to array with Array.from. That's possible by returning an array, where the first item is the key, and the second is the transformed value.

const originalMap = new Map([
  ["thing1", 1], ["thing2", 2], ["thing3", 3]
]);

const arrayMap = Array.from(originalMap, ([key, value]) => {
    return [key, value + 1]; // return an array
});

const alteredMap = new Map(arrayMap);

console.log(originalMap); // Map { 'thing1' => 1, 'thing2' => 2, 'thing3' => 3 }
console.log(alteredMap);  // Map { 'thing1' => 2, 'thing2' => 3, 'thing3' => 4 }

If you don't return that key as the first array item, you loose your Map keys.


Just use Array.from(iterable, [mapFn]).

var myMap = new Map([["thing1", 1], ["thing2", 2], ["thing3", 3]]);

var newArr = Array.from(myMap.values(), value => value + 1);

You can map() arrays, but there is no such operation for Maps. The solution from Dr. Axel Rauschmayer:

  • Convert the map into an array of [key,value] pairs.
  • Map or filter the array.
  • Convert the result back to a map.

Example:

let map0 = new Map([
  [1, "a"],
  [2, "b"],
  [3, "c"]
]);

const map1 = new Map(
  [...map0]
  .map(([k, v]) => [k * 2, '_' + v])
);

resulted in

{2 => '_a', 4 => '_b', 6 => '_c'}

You can use this function:

function mapMap(map, fn) {
  return new Map(Array.from(map, ([key, value]) => [key, fn(value, key, map)]));
}

usage:

var map1 = new Map([["A", 2], ["B", 3], ["C", 4]]);

var map2 = mapMap(map1, v => v * v);

console.log(map1, map2);
/*
Map { A ? 2, B ? 3, C ? 4 }
Map { A ? 4, B ? 9, C ? 16 }
*/

I prefer to extend the map

export class UtilMap extends Map {  
  constructor(...args) { super(args); }  
  public map(supplier) {
      const mapped = new UtilMap();
      this.forEach(((value, key) => mapped.set(key, supplier(value, key)) ));
      return mapped;
  };
}

Map.prototype.map = function(callback) {
  const output = new Map()
  this.forEach((element, key)=>{
    output.set(key, callback(element, key))
  })
  return output
}

const myMap = new Map([["thing1", 1], ["thing2", 2], ["thing3", 3]])
// no longer wishful thinking
const newMap = myMap.map((value, key) => value + 1)
console.info(myMap, newMap)

Depends on your religious fervor in avoiding editing prototypes, but, I find this lets me keep it intuitive.


You should just use Spread operator:

_x000D_
_x000D_
var myMap = new Map([["thing1", 1], ["thing2", 2], ["thing3", 3]]);_x000D_
_x000D_
var newArr = [...myMap].map(value => value[1] + 1);_x000D_
console.log(newArr); //[2, 3, 4]_x000D_
_x000D_
var newArr2 = [for(value of myMap) value = value[1] + 1];_x000D_
console.log(newArr2); //[2, 3, 4]
_x000D_
_x000D_
_x000D_