.map () карта Javascript ES6?


90

Як би ви це зробили? Інстинктивно я хочу зробити:

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 Map , FYI


Чи можете ви використовувати Array.from?
Ry-

@minitech Можливо, з поліфілом ... чи немає способу зробити це без нього?
neezer

Ну, ви можете написати власну mapфункцію для використання на ітерабелях, використовуючи for of.
Ry-

Чудово, але якщо ви дійсно збираєтесь нанести карту на карту, ви отримаєте карту в кінці. В іншому випадку ви просто спочатку перетворюєте карту в масив і здійснюєте відображення над масивом, а не .map () введення карти. Ви можете легко зіставити карту за допомогою ISO: dimap (x => [... x], x => new Map (x));
Dtipson

@ Добре, ми, мабуть, можемо написати свою власну мову програмування, але чому ..? Це досить проста річ і існує у більшості мов програмування вже багато десятиліть.
ruX

Відповіді:


74

Тож .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 вище).

Я використовую деструктуризацію масиву в позиції аргументу лямбда, щоб присвоїти ці місця масиву значенням, перш ніж повертати об'єкт для кожного el.

Якщо ви використовуєте Babel, у виробництві вам потрібно буде використовувати поліфіл браузера Babel (що включає "core-js" та "регенератор" Facebook).
Я цілком впевнений, що він містить Array.from.


Так, просто помітивши, що, схоже, мені знадобиться Array.from, щоб це зробити. Ваш перший приклад не повертає масив, до речі.
neezer

@neezer ні, це не так. .forEachпостійно повертається undefined, оскільки очікуване використання forEach- це проста ітерація на основі побічних ефектів (така ж, як і з ES5). Для того, щоб використовувати це forEach, вам потрібно створити масив і заповнити його вручну, або за допомогою згаданого, forEachабо за допомогою ітератора, поверненого .entries()або .keys( )або .values( ). Array.fromне робить нічого надзвичайно унікального, він просто приховує ту особливу потворність, що робить згаданий обхід. І так, перевірте, включивши babel-core/browser.js(або що завгодно) ви отримаєте .from...
Норгуард

4
Дивіться мою відповідь, Array.fromприймає функцію map як параметр, вам не потрібно створювати тимчасовий масив, щоб просто нанести на нього карту та відкинути.
loganfsmyth

Чи можете ви пояснити, чому об’єкту потрібні дужки, обернуті навколо нього? Я все ще трохи новачок у ES6 і маю проблеми з розумінням цієї частини. developer.mozilla.org/en-US/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, щоб використовувати перероблений Babel код на вашій сторінці у виробництві. Це ніяк не обійти, якщо ви не впровадите всі ці методи (включаючи Регенератор Facebook) самостійно, вручну ...
Норгуард,

@Norguard Зрозумів - я мав на увазі лише зміну конфігурації, яку мені потрібно буде зробити. Більше осторонь, справді. :)
neezer

5
Зазначимо, [...myMap].map(mapFn)збирається створити тимчасовий масив, а потім відкинути його. Array.fromприймає a mapFnяк другий аргумент, просто використовуйте це.
loganfsmyth

2
@wZVanG Чому ні Array.from(myMap.values(), val => val + 1);?
loganfsmyth

21

Просто використовуйте 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()взяв функцію відображення як необов’язковий другий параметр.
Андру,

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, а другий - перетворений 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 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

Ви можете зіставити масиви (), але для Карт такої операції немає. Рішення від доктора Акселя Раушмаєра :

  • Перетворіть карту у масив пар [ключ, значення].
  • Зіставити або відфільтрувати масив.
  • Перетворити результат назад на карту.

Приклад:

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щоб було зрозуміло, що ви насправді не зіставляєте об’єкт, як це, мабуть, слід було очікувати.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.