.map()Javascript ES6 Map?


89

你会怎么做?本能地,我想做:

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 }

从新的迭代协议文档中我并没有得到太多收获。

我知道wu.js,但是我正在运行一个Babel项目,并且不想包含Traceur它似乎当前依赖于Traceur

对于如何将fitzgen / wu.js如何提取到我自己的项目中,我也一无所知。

很想清楚,简洁地说明我在这里缺少什么。谢谢!


FYI的ES6地图文档


可以使用Array.from吗?
Ry-

@minitech可能,用填充工具...有没有办法做到这一点没有它?
neezer

好的,您可以使用编写自己的map函数以用于可迭代对象for of
Ry-

超级nitpick,但是如果您真的要在地图上进行地图绘制,那么您将在最后获得地图。否则,您只是首先将Map转换为Array并在Array上进行映射,而不是.map()映射。您可以使用ISO轻松地在地图上映射:dimap(x => [... x],x => new Map(x));
迪普森'16

@Ry好,我们可能可以编写我们自己的编程语言,但是为什么..?这很简单,并且在大多数编程语言中已经存在了数十年。
ruX

Answers:


73

因此,.map它本身仅提供您关心的一个价值...也就是说,有几种解决方法:

// instantiation
const myMap = new Map([
  [ "A", 1 ],
  [ "B", 2 ]
]);

// what's built into Map for you
myMap.forEach( (val, key) => console.log(key, val) ); // "A 1", "B 2"

// what Array can do for you
Array.from( myMap ).map(([key, value]) => ({ key, value })); // [{key:"A", value: 1}, ... ]

// less awesome iteration
let entries = myMap.entries( );
for (let entry of entries) {
  console.log(entry);
}

请注意,在第二个示例中,我使用了很多新内容……...Array.from进行任何可迭代的操作(任何时候使用[].slice.call( ),再加上Set和Maps)并将其转换为数组... ... Maps ,将其强制转换为数组后,将其转换为数组的数组el[0] === key && el[1] === value;(基本上,与我在上面示例Map所用的格式相同)。

我在lambda的参数位置使用数组的解构,以将这些数组点分配给值,然后为每个el返回一个对象。

如果您在生产中使用Babel,则需要使用Babel的浏览器polyfill(包括“ core-js”和Facebook的“ regenerator”)。
我很确定它包含Array.from


是的,只是注意到看起来我需Array.from要这样做。第一个示例不返回数组,顺便说一句。
neezer

@neezer不,不是。总是.forEach返回undefined平坦的状态,因为预期的使用forEach是基于简单副作用的迭代(与自ES5以来相同)。为了使用它forEach,您需要构建一个数组并通过forEach或通过.entries()or.keys( )或返回的迭代器手动填充它.values( )Array.from没有做任何弯弯曲曲的独特工作,只是在掩盖执行上述遍历的特别丑陋。是的,通过包括babel-core/browser.js(或其他内容)来检查您是否得到了.from……
Norguard 2015年

4
看到我的答案,Array.from将map函数作为参数,您不需要创建一个临时数组就可以映射并丢弃它。
loganfsmyth,2015年

您能解释一下为什么该对象需要用括号括起来吗?我对ES6还是有点陌生​​,在理解这部分内容时遇到了麻烦。developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/…表示将其解释为标签,不确定是什么意思
dtc

1
@dtc是的,当您编写一个对象const obj = { x: 1 };时,您编写一个代码块时,您在编写时if (...) { /* block */ },当您编写一个箭头函数时,您在编写时() => { return 1; },如何知道它是一个函数体,而不是对象的括号?答案就是括号。否则,它希望该名称是一个很少使用的功能的代码块的标签,但是您可以标记一个switch或一个循环,以便您可以break;按名称显示它们。因此,如果缺少它,您将有一个带有标签的函数体和1基于MDN示例的语句
Norguard

34

您应该只使用Spread运算符

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

var newArr = [...myMap].map(value => value[1] + 1);
console.log(newArr); //[2, 3, 4]

var newArr2 = [for(value of myMap) value = value[1] + 1];
console.log(newArr2); //[2, 3, 4]


看来还需要Array.from,FYI。
neezer

1
绝对@neezer,您肯定在生产中使用Babel的polyfill,以便在您的页面上使用Babel编译的代码。没有办法解决,除非您手动实现所有这些方法(包括Facebook的Regenerator)...
Norguard 2015年

@Norguard知道了-我要做的只是配置更改。确实,还有更多。:)
neezer

5
只是要注意,[...myMap].map(mapFn)将创建一个临时数组,然后将其丢弃。Array.from将amapFn作为第二个参数,只需使用它即可。
loganfsmyth,2015年

2
@wZVanG为什么不Array.from(myMap.values(), val => val + 1);呢?
loganfsmyth,2015年

21

只需使用Array.from(iterable, [mapFn])

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

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

感谢您的答复,我希望它更高。我一直使用将Map散布到临时数组中的模式,但是没有意识到Array.from()将映射函数作为可选的第二个参数。
安德鲁

4

您可以使用此功能:

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

用法:

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 }
*/

3

使用Array.from我编写了一个Typescript函数来映射值:

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 }

3

实际上Map,将转换为数组后,您仍然可以使用原始键Array.from。这可以通过返回一个数组来实现,其中第一项是key,第二项是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 }

如果不将该作为第一个数组项返回,则松开Map键。


1

我更喜欢扩展地图

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;
  };
}

1

您可以使用myMap.forEach,并在每个循环中使用map.set更改值。

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

for (var [key, value] of myMap.entries()) {
  console.log(key + ' = ' + value);
}


myMap.forEach((value, key, map) => {
  map.set(key, value+1)
})

for (var [key, value] of myMap.entries()) {
  console.log(key + ' = ' + value);
}


我认为这错了。Array.map不会改变任何东西。
亚伦

0

const mapMap = (callback, map) => new Map(Array.from(map).map(callback))

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

var newMap = mapMap((pair) => [pair[0], pair[1] + 1], myMap); // Map { 'thing1' => 2, 'thing2' => 3, 'thing3' => 4 }


0

如果您不想事先将整个Map转换为数组和/或分解键值数组,则可以使用以下愚蠢的函数:

/**
 * Map over an ES6 Map.
 *
 * @param {Map} map
 * @param {Function} cb Callback. Receives two arguments: key, value.
 * @returns {Array}
 */
function mapMap(map, cb) {
  let out = new Array(map.size);
  let i = 0;
  map.forEach((val, key) => {
    out[i++] = cb(key, val);
  });
  return out;
}

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

console.log(
  mapMap(map, (k, v) => `${k}-${v}`).join(', ')
); // a-1, b-2, c-3


0
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)

避免编辑原型取决于您的宗教热情,但是,我发现这让我保持了直观性。


0

您可以使用map()数组,但是Maps没有此类操作。Axel Rauschmayer博士的解决方案:

  • 将地图转换为[key,value]对数组。
  • 映射或过滤数组。
  • 将结果转换回地图。

例:

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

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

导致

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

0

也许这样:

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

您只需要在以下步骤进行猴子补丁Map

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

我们本可以编写此修补程序的简单形式:

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

但是我们本来会迫使我们写的m.map(([k, v]) => [k, v * 2]);,这对我来说似乎更痛苦和丑陋。

仅映射值

我们也可以只映射值,但是我不建议您选择该解决方案,因为它太具体了。不过,可以做到的,我们将拥有以下API:

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

就像在以这种方式修补之前:

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

也许您可以同时使用两者,命名第二个名称mapValues可以清楚地表明您实际上并未像预期的那样映射对象。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.