.map () eine Javascript ES6 Map?

89

Wie würdest du das machen? Instinktiv möchte ich tun:

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 }

Ich habe nicht viel aus der Dokumentation zum neuen Iterationsprotokoll gelernt .

Ich kenne wu.js , aber ich führe ein Babel- Projekt durch und möchte Traceur nicht einbeziehen , von dem es anscheinend derzeit abhängt .

Ich weiß auch nicht , wie ich fitzgen / wu.js in mein eigenes Projekt einbauen soll .

Würde eine klare, prägnante Erklärung dessen lieben, was ich hier vermisse. Vielen Dank!


Dokumente für ES6 Map , FYI

neezer
quelle
Können Sie verwenden Array.from?
Ry-
@minitech Möglicherweise mit einer Polyfüllung ... gibt es keine Möglichkeit, dies ohne sie zu tun?
Neezer
Nun, Sie könnten Ihre eigene mapFunktion für die Verwendung auf iterables mit schreiben for of.
Ry-
Super Nitpick, aber wenn Sie wirklich über eine Karte kartieren würden , würden Sie am Ende eine Karte zurückbekommen. Andernfalls konvertieren Sie zuerst eine Map in ein Array und ordnen sie einem Array zu, nicht .map () einer Map. Sie können eine Karte einfach mit einer ISO zuordnen: dimap (x => [... x], x => neue Karte (x));
Dtipson
@Ry gut, wir können wahrscheinlich unsere eigene Programmiersprache schreiben, aber warum ..? Es ist ganz einfach und existiert in den meisten Programmiersprachen seit vielen Jahrzehnten.
RuX

Antworten:

73

Es .mapselbst bietet also nur einen Wert, der Ihnen am Herzen liegt ... Es gibt jedoch einige Möglichkeiten, dies anzugehen:

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

Beachten Sie , ich bin in diesem zweiten Beispiel eine Menge neuer Sachen mit ... ... Array.fromnimmt jede iterable (jedes Mal , wenn Sie verwenden würden [].slice.call( ), sowie Sets und Maps) und wandelt es in ein Array ... ... Karten Wenn Sie zu einem Array gezwungen werden, verwandeln Sie sich in ein Array von Arrays, wobei el[0] === key && el[1] === value;(im Grunde genommen in demselben Format, mit dem ich meine Beispielkarte oben vorgefüllt habe).

Ich verwende die Destrukturierung des Arrays an der Argumentposition des Lambda, um diese Array-Spots Werten zuzuweisen, bevor ich für jedes el ein Objekt zurückgebe.

Wenn Sie Babel in der Produktion verwenden, müssen Sie Babels Browser-Polyfüllung verwenden (die "core-js" und Facebooks "regenerator" enthält).
Ich bin mir ziemlich sicher, dass es enthält Array.from.

Norguard
quelle
Ja, ich merke nur, dass es so aussieht, als würde ich das brauchen, Array.fromum das zu tun. Ihr erstes Beispiel gibt übrigens kein Array zurück.
Neezer
@neezer nein, tut es nicht. .forEachkehrt undefineddie ganze Zeit auf forEachHochtouren zurück , da die erwartete Verwendung von eine einfache, auf Nebenwirkungen basierende Iteration ist (wie seit ES5). Um dies zu verwenden, forEachmöchten Sie ein Array erstellen und von Hand forEachfüllen , entweder über diesen oder über den von .entries()oder .keys( )oder zurückgegebenen Iterator .values( ). Array.frommacht nichts Verrücktes, sondern verbirgt nur die besondere Hässlichkeit, diese Durchquerung zu machen. Und ja, überprüfen babel-core/browser.jsSie, .from
ob
4
Siehe meine Antwort, Array.fromnimmt eine Kartenfunktion als Parameter, Sie müssen kein temporäres Array erstellen, nur um es zuzuordnen und zu verwerfen.
Loganfsmyth
Können Sie erklären, warum für das Objekt Klammern erforderlich sind? Ich bin noch ein wenig neu in ES6 und habe Probleme, diesen Teil zu verstehen. developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… sagt, dass es als Label interpretiert wird und
ich
1
@dtc ja, wenn Sie ein Objekt schreiben Sie schreiben const obj = { x: 1 };, wenn Sie einen Code - Block zu schreiben, schreiben Sie if (...) { /* block */ }, wenn Sie einen Pfeil Funktion schreiben Sie schreiben () => { return 1; }, so wie weiß es , wenn es eine Funktion Körper, im Vergleich zu der Streben eines Objekts? Und die Antwort sind Klammern. Andernfalls wird erwartet, dass der Name eine Bezeichnung für einen Codeblock ist, ein selten verwendetes Feature. Sie können jedoch eine switchoder eine Schleife beschriften , damit Sie break;sie nach Namen entfernen können . Wenn es fehlt, haben Sie einen Funktionskörper mit einer Bezeichnung und der Anweisung 1, basierend auf dem MDN-Beispiel
Norguard
34

Sie sollten einfach den Spread-Operator verwenden :

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]

Walter Chapilliquen - wZVanG
quelle
Sieht so aus, als ob es noch erforderlich ist Array.from, FYI.
Neezer
1
@neezer Sie absolut positiv muss Babels polyfill verwenden, um Babel-transpiled Code auf Ihrer Seite zu verwenden, in der Produktion. Es führt kein Weg daran vorbei, es sei denn, Sie implementieren alle diese Methoden (einschließlich des Regenerators von Facebook) selbst von Hand ...
Norguard
@Norguard Verstanden - nur eine Konfigurationsänderung, die ich vornehmen muss, ist alles, was ich meinte. Eigentlich eher beiseite. :)
Neezer
5
Nur um zu beachten, [...myMap].map(mapFn)wird ein temporäres Array erstellt und dann verworfen. Array.fromnimmt a mapFnals zweites Argument, benutze das einfach.
Loganfsmyth
2
@wZVanG Warum nicht Array.from(myMap.values(), val => val + 1);?
Loganfsmyth
21

Einfach benutzen Array.from(iterable, [mapFn]).

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

var newArr = Array.from(myMap.values(), value => value + 1);
loganfsmyth
quelle
Vielen Dank für diese Antwort, ich wünschte, es wäre höher. Ich verwende das Muster, eine Karte ständig in ein temporäres Array zu verteilen, ohne es zu merken, dass ich Array.from()eine Zuordnungsfunktion als optionalen zweiten Parameter verwendet habe.
Andru
4

Sie können diese Funktion verwenden:

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

Verwendung:

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 }
*/
Yukulélé
quelle
3

Mit Array.fromIch habe eine Typescript-Funktion geschrieben, die die Werte abbildet:

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 }
saul.shanabrook
quelle
3

Tatsächlich können Sie Mapnach dem Konvertieren in ein Array mit noch einen mit den Originalschlüsseln haben Array.from. Dies ist möglich, indem ein Array zurückgegeben wird , bei dem das erste Element keydas transformierte und das zweite das transformierte Element ist 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 }

Wenn Sie diesen Schlüssel nicht als erstes Array-Element zurückgeben, verlieren Sie Ihre MapSchlüssel.

mayid
quelle
1

Ich ziehe es vor, die Karte zu erweitern

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;
  };
}
Nils Rösel
quelle
1

Sie können myMap.forEach verwenden und in jeder Schleife map.set verwenden, um den Wert zu ändern.

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

Jäger Liu
quelle
Ich denke, das geht am eigentlichen Punkt vorbei. Array.map mutiert nichts.
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 }

David Braun
quelle
0

Wenn Sie nicht vorher die gesamte Map in ein Array konvertieren und / oder Schlüsselwert-Arrays zerstören möchten, können Sie diese dumme Funktion verwenden:

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

mpen
quelle
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)

Hängt von Ihrer religiösen Leidenschaft ab, wenn Sie vermeiden, Prototypen zu bearbeiten, aber ich finde, dass ich dies intuitiv halten kann.

Commi
quelle
0

Sie können () Arrays zuordnen, für Maps gibt es jedoch keine solche Operation. Die Lösung von Dr. Axel Rauschmayer :

  • Konvertieren Sie die Karte in ein Array von [Schlüssel, Wert] -Paaren.
  • Ordnen Sie das Array zu oder filtern Sie es.
  • Konvertieren Sie das Ergebnis zurück in eine Karte.

Beispiel:

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

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

führte zu

{2 => '_a', 4 => '_b', 6 => '_c'}
römisch
quelle
0

Vielleicht so:

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

Sie müssten nur Mapvorher einen Affen-Patch ausführen:

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

Wir hätten eine einfachere Form dieses Patches schreiben können:

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

Aber wir hätten uns gezwungen zu schreiben m.map(([k, v]) => [k, v * 2]); was mir etwas schmerzhafter und hässlicher erscheint.

Nur Werte zuordnen

Wir könnten auch nur Werte zuordnen, aber ich würde nicht raten, diese Lösung zu wählen, da sie zu spezifisch ist. Trotzdem kann es gemacht werden und wir hätten die folgende API:

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

Genau wie vor dem Patchen auf diese Weise:

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

Vielleicht können Sie beides haben, indem Sie das zweite benennen mapValues, um zu verdeutlichen, dass Sie das Objekt nicht tatsächlich so zuordnen, wie es wahrscheinlich zu erwarten wäre.

cglacet
quelle