简体   繁体   English

.map() 一个 Javascript ES6 Map?

[英].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 .我知道wu.js ,但我正在运行一个Babel项目并且不想包含Traceur它似乎目前依赖于.

I also am a bit clueless as to how to extract how fitzgen/wu.js did it into my own project.对于如何将fitzgen/wu.js如何提取到我自己的项目中,我也有点不知所措。

Would love a clear, concise explanation of what I'm missing here.希望对我在这里缺少的内容进行清晰,简洁的解释。 Thanks!谢谢!


Docs for ES6 Map , FYI ES6 Map 的文档,仅供参考

So .map itself only offers one value you care about... That said, there are a few ways of tackling this:所以.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);
}

Note, I'm using a lot of new stuff in that second example... ... Array.from takes any iterable (any time you'd use [].slice.call( ) , plus Sets and Maps) and turns it into an array... ...Maps, when coerced into an array, turn into an array of arrays, where el[0] === key && el[1] === value;注意,我在第二个例子中使用了很多新东西...... ... Array.from接受任何迭代(任何时候你使用[].slice.call( ) ,加上 Sets 和 Maps)并轮流把它变成数组... ...Maps,当强制变成数组时,变成数组的数组,其中el[0] === key && el[1] === value; (basically, in the same format that I prefilled my example Map with, above). (基本上,与我在上面预填充示例 Map 的格式相同)。

I'm using destructuring of the array in the argument position of the lambda, to assign those array spots to values, before returning an object for each el.我在 lambda 的参数位置使用数组的解构,将这些数组点分配给值,然后为每个 el 返回一个对象。

If you're using Babel, in production, you're going to need to use Babel's browser polyfill (which includes "core-js" and Facebook's "regenerator").如果您在生产环境中使用 Babel,您将需要使用 Babel 的浏览器 polyfill(包括“core-js”和 Facebook 的“regenerator”)。
I'm quite certain it contains Array.from .我很确定它包含Array.from

You should just use Spread operator :您应该只使用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]

Just use Array.from(iterable, [mapFn]) .只需使用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);

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

Using Array.from I wrote a Typescript function that maps the values:使用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 }

Actually you can still have a Map with the original keys after converting to array with Array.from .其实你仍然可以有一个Map与原来的键转换成数组后Array.from That's possible by returning an array , where the first item is the key , and the second is the transformed value .这可以通过返回一个array 来实现,其中第一项是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 }

If you don't return that key as the first array item, you loose your Map keys.如果您不将该作为第一个数组项返回,就会丢失您的Map键。

You can map() arrays, but there is no such operation for Maps.您可以 map() 数组,但 Maps 没有这样的操作。 The solution from Dr. Axel Rauschmayer : Axel Rauschmayer 博士的解决方案:

  • Convert the map into an array of [key,value] pairs.将映射转换为 [key,value] 对的数组。
  • 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'}

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

You can use myMap.forEach, and in each loop, using map.set to change value.您可以使用 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); }

 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 }

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

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.取决于您避免编辑原型的宗教热情,但是,我发现这可以让我保持直观。

In typescript, in case somebody would need it :在打字稿中,以防有人需要它:

export {}

declare global {
    interface Map<K, V> {
        map<T>(predicate: (key: K, value: V) => T): Map<V, T>
    }
}

Map.prototype.map = function<K, V, T>(predicate: (value: V, key: K) => T): Map<K, T> {
    let map: Map<K, T> = new Map()

    this.forEach((value: V, key: K) => {
        map.set(key, predicate(value, key))
    })
    return map
}

Here is a Typescript variant which builds upon some of the existing ideas and attempts to be flexible in the following ways:这是一个 Typescript 变体,它建立在一些现有想法的基础上,并尝试通过以下方式变得灵活:

  • Both key and value types of output map can differ from input output map 的键和值类型都可以与输入不同
  • The mapper function can access the input map itself, if it likes映射器 function 可以访问输入 map 本身,如果它喜欢
function mapMap<TKI, TVI, TKO, TVO>(map: Map<TKI, TVI>,
        f: (k: TKI, v: TVI, m: Map<TKI, TVI>) => [TKO, TVO]) : Map<TKO, TVO>{
    return new Map([...map].map(p => f(p[0], p[1], map)))
}

Note: this code uses the spread operator, like some of the existing ideas, and so needs a target of of 'es2015' or higher according to my VS Code Intellisense.注意:此代码使用扩展运算符,就像一些现有的想法一样,因此根据我的 VS Code Intellisense 需要一个of 'es2015' or higher的目标。

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进行猴子补丁:

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]);但是我们会强迫我们写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:尽管如此,它可以完成,我们将拥有以下 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.也许您可以同时拥有两者,命名第二个mapValues以表明您实际上并没有像预期的那样映射对象。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM