【问题标题】:.map() a Javascript ES6 Map?.map() 一个 JavaScript ES6 地图?
【发布时间】:2015-09-14 01:59:25
【问题描述】:

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

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 }

我没有从documentation on the new iteration protocol 收集到太多信息。

我知道wu.js,但我正在运行一个Babel 项目并且不想包含Traceur,即it seems like it currently depends on

对于如何将how fitzgen/wu.js did it 提取到我自己的项目中,我也有点不知所措。

希望对我在这里缺少的内容进行清晰、简洁的解释。谢谢!


Docs for ES6 Map,仅供参考

【问题讨论】:

  • 你能用Array.from吗?
  • @minitech 可能有polyfill...没有它就没有办法吗?
  • 好吧,你可以编写自己的 map 函数用于可迭代对象,使用 for of
  • 超级挑剔,但如果你真的要在地图上map,最后你会得到一个地图。否则,您只是首先将 Map 转换为 Array 并映射到 Array,而不是 .map() 映射 Map。您可以使用 ISO 轻松映射 Map:dimap(x=>[...x], x=> new Map(x));
  • @Ry 好吧,我们可能可以编写自己的编程语言,但是为什么..?这是相当简单的事情,并且在大多数编程语言中都存在了几十年。

标签: javascript ecmascript-6


【解决方案1】:

所以.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( ),加上集合和映射)并将其转换为数组... ...映射,当强制转换为数组时,转换为数组,其中el[0] === key && el[1] === value;(基本上,与我在上面预填充示例 Map 的格式相同)。

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

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

【讨论】:

  • 是的,只是注意到看起来我需要Array.from 才能做到这一点。顺便说一句,您的第一个示例不返回数组。
  • @neezer 不,它没有。 .forEach 一直返回 undefined,因为 forEach 的预期用途是一个简单的基于副作用的迭代(与 ES5 以来相同)。为了使用forEach,您需要构建一个数组并手动填充它,或者通过上述forEach 或通过.entries().keys( ).values( ) 返回的迭代器。 Array.from 并没有做任何令人费解的独特事情,它只是隐藏了进行上述遍历的特别丑陋。是的,通过包含babel-core/browser.js(或其他任何内容)来检查您是否得到.from...
  • 看我的回答,Array.from 将 map 函数作为参数,你不需要创建一个临时数组来映射它并丢弃它。
  • 你能解释一下为什么对象需要用括号括起来吗?我对 ES6 还是有点陌生​​,无法理解那部分。 developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… 说它被解释为一个标签,我不确定这是什么意思
  • @dtc 是的,写对象的时候写const obj = { x: 1 };,写代码块的时候写if (...) { /* block */ },写箭头函数的时候写() => { return 1; },所以它如何知道它何时是函数体,而不是对象的大括号?答案是括号。否则,它希望该名称是代码块的标签,这是一个很少使用的功能,但您可以标记switch 或循环,以便您可以通过名称将break; 从它们中取出。因此,如果缺少它,您将拥有一个带有标签的函数体和声明 1,基于 MDN 示例
【解决方案2】:

您应该只使用 Spread operator

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,仅供参考。
  • @neezer 你绝对,肯定必须使用 Babel 的 polyfill,以便在你的页面上使用 Babel 转译的代码,在生产中。没有办法解决它,除非你自己手动实现所有这些方法(包括 Facebook 的 Regenerator)......
  • @Norguard 明白了——我需要做的只是一个配置更改。更多的是旁白,真的。 :)
  • 请注意,[...myMap].map(mapFn) 将创建一个临时数组,然后将其丢弃。 Array.frommapFn 作为第二个参数,使用它即可。
  • @wZVanG 为什么不Array.from(myMap.values(), val => val + 1);
【解决方案3】:

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

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

var newEntries = Array.from(myMap, ([key, value]) => [key, value + 1]);
var newMap = new Map(newEntries);

【讨论】:

  • 感谢您的回答,我希望它更高。我一直使用将 Map 传播到临时数组中的模式,幸好Array.from() 将映射函数作为可选的第二个参数。
  • 这太好了。当使用 Map.values() 时,它也与 Flow 配合得很好,不像 Array.values() still 没有:facepalm:
  • 我想这个答案一定是 OP 选择的。因为这是唯一且正确的方法。
【解决方案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 }
*/

【讨论】:

    【解决方案5】:

    使用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 }
    

    【讨论】:

      【解决方案6】:

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

      如果您不将该 key 作为第一个数组项返回,则会丢失您的 Map 键。

      【讨论】:

        【解决方案7】:

        你可以 map() 数组,但是 Maps 没有这样的操作。来自Dr. 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'}
        

        【讨论】:

          【解决方案8】:

          我更喜欢扩展地图

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

          【讨论】:

            【解决方案9】:

            您可以使用 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 不会改变任何东西。
            【解决方案10】:

            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 }

            【讨论】:

              【解决方案11】:

              如果您不想事先将整个 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

              【讨论】:

                【解决方案12】:
                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)
                

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

                【讨论】:

                  【解决方案13】:

                  也许这样:

                  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]) =&gt; [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 以明确您实际上并未像预期的那样映射对象。

                  【讨论】:

                    猜你喜欢
                    • 2023-01-23
                    • 2017-12-29
                    • 1970-01-01
                    • 1970-01-01
                    • 2015-06-08
                    • 2018-01-02
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    相关资源
                    最近更新 更多