.map () แผนที่ Javascript ES6?


98

คุณจะทำอย่างไร โดยสัญชาตญาณฉันต้องการทำ:

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ซึ่งดูเหมือนว่ามันกำลังจะขึ้นอยู่กับ

ฉันยังไม่รู้ว่าจะแยกวิธีการที่ fitzgen / wu.js ทำอย่างไรในโครงการของฉัน

ชอบคำอธิบายที่ชัดเจนและกระชับเกี่ยวกับสิ่งที่ฉันพลาดที่นี่ ขอบคุณ!


เอกสารสำหรับแผนที่ ES6 , FYI


คุณสามารถใช้Array.from?
Ry-

@minitech อาจมีpolyfill ... จะมีวิธีการที่จะไม่ทำเช่นนี้โดยไม่ได้หรือไม่?
neezer

คุณสามารถเขียนmapฟังก์ชันของคุณเองเพื่อใช้กับ iterables โดยใช้for ofไฟล์.
Ry-

Super nitpick แต่ถ้าคุณกำลังจะทำแผนที่บนแผนที่จริงๆคุณจะได้รับแผนที่คืนในตอนท้าย มิฉะนั้นคุณจะต้องแปลงแผนที่เป็น Array เป็นครั้งแรกและทำการแมปบน Array ไม่ใช่. map () ในแผนที่ คุณสามารถจับคู่แผนที่ได้อย่างง่ายดายโดยใช้ ISO: dimap (x => [... x], x => แผนที่ใหม่ (x));
Dtipson

@ เออเราคงเขียนภาษาโปรแกรมเองได้ แต่ทำไม .. ? มันค่อนข้างง่ายและมีอยู่ในภาษาโปรแกรมส่วนใหญ่มานานหลายสิบปี
ruX

คำตอบ:


79

ดังนั้น.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;(โดยทั่วไปเป็นรูปแบบเดียวกับที่ฉันเติมแผนที่ตัวอย่างไว้ล่วงหน้าด้วยด้านบน)

ฉันใช้การทำลายโครงสร้างอาร์เรย์ในตำแหน่งอาร์กิวเมนต์ของแลมบ์ดาเพื่อกำหนดจุดอาร์เรย์เหล่านั้นให้กับค่าก่อนที่จะส่งคืนวัตถุสำหรับแต่ละ el

หากคุณใช้ Babel ในการผลิตคุณจะต้องใช้ polyfill เบราว์เซอร์ของ Babel (ซึ่งรวมถึง "core-js" และ "regenerator" ของ Facebook)
Array.fromผมค่อนข้างแน่ใจว่ามันมี


ใช่แค่สังเกตว่าดูเหมือนว่าฉันจะต้องArray.fromทำสิ่งนี้ ตัวอย่างแรกของคุณไม่ส่งคืนอาร์เรย์ btw
neezer

@neezer ไม่มันไม่ได้ .forEachคืนค่าคงที่undefinedตลอดเวลาเนื่องจากการใช้งานที่คาดไว้forEachคือการทำซ้ำตามผลข้างเคียงที่เรียบง่าย (เช่นเดียวกับที่มีมาตั้งแต่ ES5) เพื่อที่จะใช้ที่forEachคุณต้องการที่จะสร้างอาร์เรย์และเติมด้วยมือทั้งผ่านการกล่าวforEachหรือผ่าน iterator กลับโดย.entries()หรือหรือ.keys( ) ไม่ได้ทำอะไรที่ไม่เหมือนใคร แต่เป็นการซ่อนความอัปลักษณ์โดยเฉพาะจากการทำข้ามผ่านดังกล่าว และใช่ตรวจสอบว่าโดยรวม(หรืออะไรก็ได้) คุณจะได้รับ....values( )Array.frombabel-core/browser.js.from
Norguard

4
ดูคำตอบของฉันArray.fromใช้ฟังก์ชันแผนที่เป็นพารามิเตอร์คุณไม่จำเป็นต้องสร้างอาร์เรย์ชั่วคราวเพียงเพื่อแมปทับและทิ้งไป
loganfsmyth

คุณอธิบายได้ไหมว่าทำไมวัตถุถึงต้องมีวงเล็บล้อมรอบวัตถุนั้น ฉันยังใหม่กับ ES6 เล็กน้อยและมีปัญหาในการทำความเข้าใจส่วนนั้น developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…บอกว่ามันถูกตีความว่าเป็นป้ายกำกับและฉันไม่แน่ใจว่ามันหมายถึงอะไร
dtc

1
@dtc ใช่เมื่อคุณเขียนวัตถุที่คุณเขียนconst obj = { x: 1 };เมื่อคุณเขียนบล็อกของโค้ดคุณเขียนif (...) { /* block */ }เมื่อคุณเขียนฟังก์ชันลูกศรที่คุณเขียน() => { return 1; }ดังนั้นจะรู้ได้อย่างไรว่าเมื่อใดเป็นเนื้อความของฟังก์ชันเทียบกับวงเล็บปีกกาของวัตถุ? และคำตอบคือวงเล็บ มิฉะนั้นคาดว่าชื่อจะเป็นป้ายกำกับสำหรับโค้ดบล็อกซึ่งเป็นคุณลักษณะที่ไม่ค่อยมีการใช้งาน แต่คุณสามารถติดป้ายกำกับ a switchหรือ loop เพื่อให้คุณสามารถแยกbreak;ออกจากชื่อได้ ดังนั้นถ้ามันหายไปแสดงว่าคุณมีเนื้อความฟังก์ชันพร้อมป้ายกำกับและคำสั่ง1ตามตัวอย่าง MDN
Norguard

35

คุณควรใช้ตัวดำเนินการ 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.fromFYI
neezer

1
@neezer คุณต้องใช้ polyfill ของ Babel อย่างแน่นอนเพื่อที่จะใช้ Babel-Transpiled Code บนเพจของคุณในการผลิต ไม่มีทางหลีกเลี่ยงได้เว้นแต่คุณจะใช้วิธีการเหล่านั้นทั้งหมด (รวมถึง Regenerator ของ Facebook) ด้วยตัวเองด้วยมือ ...
Norguard

@Norguard เข้าใจแล้ว - แค่เปลี่ยนการกำหนดค่าที่ฉันต้องทำก็คือทั้งหมดที่ฉันหมายถึง มากกว่ากันจริงๆ :)
neezer

5
โปรดทราบว่า[...myMap].map(mapFn)กำลังจะสร้างอาร์เรย์ชั่วคราวแล้วทิ้งไป Array.fromใช้mapFnเป็นอาร์กิวเมนต์ที่สองเพียงใช้สิ่งนั้น
loganfsmyth

2
@wZVanG ทำไมไม่Array.from(myMap.values(), val => val + 1);?
loganfsmyth

22

เพียงแค่ใช้Array.from(iterable, [mapFn]).

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

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

ขอบคุณสำหรับคำตอบนี้ฉันหวังว่ามันจะสูงกว่านี้ ฉันใช้รูปแบบของการแพร่กระจายแผนที่ไปยังอาร์เรย์ชั่วคราวตลอดเวลาโดยไม่รู้ตัวเลยว่าArray.from()ใช้ฟังก์ชันการแมปเป็นพารามิเตอร์ตัวเลือกที่สอง
Andru

นี่เป็นสิ่งที่ดีมาก และมันก็เล่นได้ดีกับ Flow เช่นกันเมื่อใช้Map.values()ไม่เหมือน Array.values ​​() ซึ่งยังไม่: facepalm:
ericsoco

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นั่นเป็นไปได้โดยการส่งคืนอาร์เรย์ที่รายการแรกเป็นและที่สองคือการเปลี่ยนkeyvalue

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 ไม่กลายพันธุ์อะไร
Aaron


0

หากคุณไม่ต้องการแปลงแผนที่ทั้งหมดเป็นอาร์เรย์ล่วงหน้าและ / หรือทำลายโครงสร้างอาร์เรย์คีย์ - ค่าคุณสามารถใช้ฟังก์ชันโง่ ๆ นี้:

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

คุณสามารถแมป () อาร์เรย์ แต่ไม่มีการดำเนินการดังกล่าวสำหรับ Maps วิธีแก้ปัญหาจากดร. Axel Rauschmayer :

  • แปลงแผนที่เป็นอาร์เรย์ของคู่ [คีย์ค่า]
  • แมปหรือกรองอาร์เรย์
  • แปลงผลลัพธ์กลับเป็นแผนที่

ตัวอย่าง:

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

-1

อาจเป็นวิธีนี้:

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เพื่อให้ชัดเจนว่าคุณไม่ได้ทำแผนที่วัตถุอย่างที่คาดไว้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.