.map () một Bản đồ Javascript ES6?


90

Bạn sẽ làm điều này như thế nào? Theo bản năng, tôi muốn làm:

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 }

Tôi đã không thu thập được nhiều từ tài liệu về giao thức lặp mới .

Tôi biết về wu.js , nhưng tôi đang chạy một dự án Babel và không muốn bao gồm Traceur , có vẻ như nó hiện đang phụ thuộc vào .

Tôi cũng hơi khó hiểu về cách trích xuất cách fitzgen / wu.js đã thực hiện nó vào dự án của riêng tôi.

Rất thích một lời giải thích rõ ràng, ngắn gọn về những gì tôi đang thiếu ở đây. Cảm ơn!


Tài liệu cho Bản đồ ES6 , FYI


Bạn có thể sử dụng Array.from?
Ry-

@minitech Có thể, với polyfill ... không có cách nào để làm điều này nếu không có nó?
neezer

Chà, bạn có thể viết maphàm của riêng mình để sử dụng trên các tệp lặp, bằng cách sử dụng for of.
Ry-

Super nitpick, nhưng nếu bạn thực sự định lập bản đồ trên Bản đồ, bạn sẽ nhận lại được Bản đồ ở cuối. Nếu không, trước tiên bạn chỉ chuyển đổi Bản đồ thành Mảng và ánh xạ qua Mảng, không phải .map () nhập vào Bản đồ. Bạn có thể dễ dàng lập bản đồ trên Bản đồ bằng cách sử dụng ISO: dimap (x => [... x], x => new Map (x));
Dtipson

@Ry well, chúng ta có thể viết ngôn ngữ lập trình của riêng mình, nhưng tại sao ..? Nó khá đơn giản và tồn tại trong hầu hết các ngôn ngữ lập trình trong nhiều thập kỷ.
ruX

Câu trả lời:


74

Vì vậy, .mapbản thân nó chỉ cung cấp một giá trị mà bạn quan tâm ... Điều đó nói rằng, có một số cách để giải quyết vấn đề này:

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

Lưu ý, tôi đang sử dụng rất nhiều thứ mới trong ví dụ thứ hai đó ... ... Array.fromlấy bất kỳ thứ gì có thể lặp lại (bất kỳ lúc nào bạn sử dụng [].slice.call( ), cộng với Bộ và Bản đồ) và biến nó thành một mảng ... ... Bản đồ , khi bị ép buộc vào một mảng, hãy biến thành một mảng mảng, trong đó el[0] === key && el[1] === value;(về cơ bản, ở cùng một định dạng mà tôi đã điền sẵn Bản đồ ví dụ của mình ở trên).

Tôi đang sử dụng cấu trúc hủy của mảng ở vị trí đối số của lambda, để gán các điểm mảng đó cho các giá trị, trước khi trả về một đối tượng cho mỗi el.

Nếu bạn đang sử dụng Babel, trong quá trình sản xuất, bạn sẽ cần sử dụng polyfill trình duyệt của Babel (bao gồm "core-js" và "tái tạo" của Facebook).
Tôi khá chắc chắn nó có chứa Array.from.


Vâng, chỉ cần lưu ý rằng có vẻ như tôi sẽ cần Array.fromđể làm điều này. Ví dụ đầu tiên của bạn không trả về một mảng, btw.
neezer

@neezer không, nó không. .forEachtrả về undefinedphẳng, mọi lúc, vì việc sử dụng mong đợi forEachlà một phép lặp dựa trên hiệu ứng phụ đơn giản (giống như nó đã có từ ES5). Để sử dụng điều đó forEach, bạn muốn tạo một mảng và điền nó bằng tay, thông qua đã nói forEachhoặc thông qua trình lặp được trả về bởi .entries()hoặc .keys( )hoặc .values( ). Array.fromkhông phải là làm bất cứ điều gì độc nhất vô nhị, nó chỉ che giấu sự xấu xí đặc biệt của việc thực hiện hành vi đã nói. Và vâng, kiểm tra bằng cách bao gồm babel-core/browser.js(hoặc bất cứ điều gì) bạn sẽ có được .from...
Norguard

4
Hãy xem câu trả lời của tôi, Array.fromlấy một hàm bản đồ làm tham số, bạn không cần tạo một mảng tạm thời chỉ để ánh xạ lên nó và loại bỏ nó.
loganfsmyth

Bạn có thể giải thích tại sao đối tượng cần một dấu ngoặc đơn bao quanh nó không? Tôi vẫn còn một chút mới với ES6 và gặp khó khăn khi hiểu phần đó. developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… nói rằng nó được hiểu là một nhãn và tôi không chắc điều đó có nghĩa là gì
dtc

1
@dtc vâng, khi bạn viết một đối tượng thì bạn viết const obj = { x: 1 };, khi bạn viết một khối mã thì bạn viết if (...) { /* block */ }, khi bạn viết một hàm mũi tên thì bạn viết () => { return 1; }, vậy làm sao nó biết khi nào đó là phần thân của hàm, so với dấu ngoặc nhọn của một đối tượng? Và câu trả lời là dấu ngoặc đơn. Nếu không, nó hy vọng rằng tên là nhãn cho một khối mã, một tính năng hiếm khi được sử dụng, nhưng bạn có thể gắn nhãn cho một switchhoặc một vòng lặp để bạn có thể break;loại bỏ chúng theo tên. Vì vậy, nếu nó mất tích, bạn có một cơ quan chức năng, với một nhãn, và báo cáo kết quả 1, dựa trên ví dụ MDN
Norguard

34

Bạn chỉ nên sử dụng toán tử 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]


Có vẻ như nó vẫn yêu cầu Array.from, FYI.
neezer

1
@neezer, bạn hoàn toàn phải sử dụng polyfill của Babel, để sử dụng mã chuyển vị Babel trên trang của bạn, trong quá trình sản xuất. Không có cách nào xung quanh nó, trừ khi bạn thực hiện tất cả các phương pháp đó (bao gồm cả Regenerator của Facebook) của chính mình, bằng tay ...
Norguard

@Norguard OK - tôi chỉ cần thay đổi cấu hình là xong. Hơn một bên, thực sự. :)
neezer

5
Chỉ cần lưu ý, [...myMap].map(mapFn)sẽ tạo một mảng tạm thời và sau đó loại bỏ nó. Array.fromlấy a mapFnlàm đối số thứ hai, chỉ cần sử dụng điều đó.
loganfsmyth

2
@wZVanG Tại sao không Array.from(myMap.values(), val => val + 1);?
loganfsmyth

21

Chỉ cần sử dụng Array.from(iterable, [mapFn]).

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

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

Cảm ơn câu trả lời này, tôi ước nó cao hơn. Tôi luôn sử dụng mô hình trải rộng Bản đồ thành một mảng tạm thời, thực sự không hề hay biết Array.from()đã lấy một hàm ánh xạ làm tham số thứ hai tùy chọn.
Andru

4

Bạn có thể sử dụng chức năng này:

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

sử dụng:

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

Bằng cách sử dụng, Array.fromtôi đã viết một hàm Typecript ánh xạ các giá trị:

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

Trên thực tế, bạn vẫn có thể có một Mapvới các khóa ban đầu sau khi chuyển đổi thành mảng với Array.from. Điều đó có thể thực hiện được bằng cách trả về một mảng , trong đó mục đầu tiên là keyvà mục thứ hai là biến đổi 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 }

Nếu bạn không trả lại khóa đó dưới dạng mục mảng đầu tiên, bạn đã mất Mapchìa khóa của mình .


1

Tôi thích mở rộng bản đồ hơn

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

Bạn có thể sử dụng myMap.forEach và trong mỗi vòng lặp, sử dụng map.set để thay đổi giá trị.

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


Tôi nghĩ rằng điều này không chính xác. Array.map không thay đổi bất cứ điều gì.
Aaron

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

Nếu bạn không muốn chuyển đổi toàn bộ Bản đồ thành một mảng trước đó và / hoặc hủy cấu trúc các mảng khóa-giá trị, bạn có thể sử dụng hàm ngớ ngẩn này:

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

Phụ thuộc vào lòng nhiệt thành tôn giáo của bạn trong việc tránh chỉnh sửa nguyên mẫu, nhưng, tôi thấy điều này cho phép tôi giữ nó trực quan.


0

Bạn có thể ánh xạ () mảng, nhưng không có thao tác như vậy đối với Maps. Giải pháp từ Tiến sĩ Axel Rauschmayer :

  • Chuyển đổi bản đồ thành một mảng các cặp [khóa, giá trị].
  • Ánh xạ hoặc lọc mảng.
  • Chuyển đổi kết quả trở lại bản đồ.

Thí dụ:

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

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

dẫn đến

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

0

Có thể theo cách này:

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

Bạn chỉ cần vá khỉ Maptrước:

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

Chúng tôi có thể đã viết một dạng đơn giản hơn của bản vá này:

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

Nhưng chúng tôi buộc chúng tôi phải viết m.map(([k, v]) => [k, v * 2]); đó có vẻ đau đớn và xấu xí hơn đối với tôi.

Chỉ ánh xạ giá trị

Chúng tôi cũng có thể chỉ lập bản đồ các giá trị, nhưng tôi không khuyên bạn nên chọn giải pháp đó vì nó quá cụ thể. Tuy nhiên, nó có thể được thực hiện và chúng tôi sẽ có API sau:

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

Giống như trước khi vá theo cách này:

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

Có lẽ bạn có thể có cả hai, đặt tên thứ hai mapValuesđể làm rõ rằng bạn không thực sự ánh xạ đối tượng như nó có thể mong đợi.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.