Jak byś to zrobił? Instynktownie chcę zrobić:
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 }
Nie zebrałem zbyt wiele z dokumentacji nowego protokołu iteracji .
Znam wu.js , ale prowadzę projekt Babel i nie chcę włączać Traceur , od którego wydaje się, że obecnie zależy .
Nie mam też pojęcia, jak wyodrębnić sposób, w jaki fitzgen / wu.js zrobił to w moim własnym projekcie.
Chciałbym jasne, zwięzłe wyjaśnienie tego, czego tutaj brakuje. Dzięki!
Dokumenty dla mapy ES6 , do Twojej wiadomości
javascript
ecmascript-6
neezer
źródło
źródło
Array.from
?map
funkcję do użycia w iterable, używającfor of
.Odpowiedzi:
Więc
.map
sam w sobie oferuje tylko jedną wartość, na której Ci zależy ... To powiedziawszy, jest kilka sposobów rozwiązania tego problemu:// 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); }
Uwaga, w tym drugim przykładzie używam wielu nowych rzeczy ... ...
Array.from
pobiera dowolne iterowalne elementy (za każdym razem, gdy używasz[].slice.call( )
, oraz zestawy i mapy) i zamienia je w tablicę ... ... Mapy , po przekształceniu w tablicę zamienia się w tablicę tablic, gdzieel[0] === key && el[1] === value;
(w zasadzie w tym samym formacie, w którym wstępnie wypełniłem moją przykładową Mapę, powyżej).Używam destrukturyzacji tablicy w pozycji argumentu lambdy, aby przypisać te miejsca tablicy do wartości, przed zwróceniem obiektu dla każdego el.
Jeśli używasz Babel w produkcji, będziesz musiał użyć wypełnienia przeglądarki Babel (który zawiera „core-js” i „regenerator” Facebooka).
Jestem całkiem pewien, że zawiera
Array.from
.źródło
Array.from
, aby to zrobić. Twój pierwszy przykład nie zwraca tablicy, przy okazji..forEach
zwraca sięundefined
całkowicie, przez cały czas, ponieważ oczekiwane użycieforEach
jest prostą iteracją opartą na efektach ubocznych (tak samo jak od czasu ES5). Aby z tego skorzystaćforEach
, chciałbyś zbudować tablicę i zapełnić ją ręcznie, albo przez said,forEach
albo przez iterator zwracany przez.entries()
lub.keys( )
lub.values( )
.Array.from
nie robi niczego niezwykłego, po prostu ukrywa tę szczególną brzydotę wykonywania wspomnianego przemierzania. I tak, sprawdź to,babel-core/browser.js
.from
Array.from
przyjmuje funkcję mapy jako parametr, nie musisz tworzyć tymczasowej tablicy tylko po to, aby ją zmapować i odrzucić.const obj = { x: 1 };
, kiedy piszesz blok kodu, piszeszif (...) { /* block */ }
, kiedy piszesz funkcję strzałkową, którą piszesz() => { return 1; }
, więc skąd ona wie, kiedy jest to treść funkcji, w porównaniu z nawiasami klamrowymi obiektu? A odpowiedź to nawiasy. W przeciwnym razie oczekuje, że nazwa jest etykietą dla rzadko używanego bloku kodu, ale można oznaczyćswitch
pętlę lub pętlę, aby możnabreak;
było z nich wyjść po nazwie. Więc jeśli go brakuje, masz1
Powinieneś po prostu użyć operatora 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]
źródło
Array.from
, FYI.[...myMap].map(mapFn)
utworzy tymczasową tablicę, a następnie ją odrzuci.Array.from
przyjmujemapFn
jako drugi argument, po prostu użyj tego.Array.from(myMap.values(), val => val + 1);
?Po prostu użyj
Array.from(iterable, [mapFn])
.var myMap = new Map([["thing1", 1], ["thing2", 2], ["thing3", 3]]); var newArr = Array.from(myMap.values(), value => value + 1);
źródło
Array.from()
wziął funkcję mapowania jako opcjonalny drugi parametr.Możesz użyć tej funkcji:
function mapMap(map, fn) { return new Map(Array.from(map, ([key, value]) => [key, fn(value, key, map)])); }
stosowanie:
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 } */
źródło
Używając
Array.from
napisałem funkcję Typescript, która mapuje wartości: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 }
źródło
Właściwie nadal możesz mieć
Map
z oryginalnymi kluczami po konwersji na tablicę zArray.from
. Jest to możliwe, zwracając tablicę , w której pierwszy element tokey
, a drugi to przekształcenievalue
.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 }
Jeśli nie zwrócisz tego klucza jako pierwszego elementu tablicy, stracisz
Map
klucze.źródło
Wolę rozszerzać 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; }; }
źródło
Możesz użyć myMap.forEach, aw każdej pętli użyć map.set do zmiany wartości.
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); }
źródło
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 }
źródło
Jeśli nie chcesz wcześniej konwertować całej mapy na tablicę i / lub zniszczyć tablice klucz-wartość, możesz użyć tej głupiej funkcji:
/** * 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
źródło
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)
Zależy od twojego religijnego zapału w unikaniu edycji prototypów, ale uważam, że pozwala mi to zachować intuicję.
źródło
Możesz mapować () tablice, ale nie ma takiej operacji dla Map. Rozwiązanie Dr. Axela Rauschmayera :
Przykład:
let map0 = new Map([ [1, "a"], [2, "b"], [3, "c"] ]); const map1 = new Map( [...map0] .map(([k, v]) => [k * 2, '_' + v]) );
doprowadzony
{2 => '_a', 4 => '_b', 6 => '_c'}
źródło
Może w ten sposób:
const m = new Map([["a", 1], ["b", 2], ["c", 3]]); m.map((k, v) => [k, v * 2]); // Map { 'a' => 2, 'b' => 4, 'c' => 6 }
Musisz tylko do plastra małpa
Map
przed:Map.prototype.map = function(func){ return new Map(Array.from(this, ([k, v]) => func(k, v))); }
Mogliśmy napisać prostszą formę tej łatki:
Map.prototype.map = function(func){ return new Map(Array.from(this, func)); }
Zmusilibyśmy nas jednak do napisania,
m.map(([k, v]) => [k, v * 2]);
co wydaje mi się nieco bardziej bolesne i brzydkie.Mapowanie tylko wartości
Moglibyśmy również mapować tylko wartości, ale nie radziłbym wybierać tego rozwiązania, ponieważ jest zbyt szczegółowe. Niemniej jednak można to zrobić i mielibyśmy następujące API:
const m = new Map([["a", 1], ["b", 2], ["c", 3]]); m.map(v => v * 2); // Map { 'a' => 2, 'b' => 4, 'c' => 6 }
Tak jak przed łataniem w ten sposób:
Map.prototype.map = function(func){ return new Map(Array.from(this, ([k, v]) => [k, func(v)])); }
Może możesz mieć oba, nazywając drugą,
mapValues
aby było jasne, że w rzeczywistości nie mapujesz obiektu, jak prawdopodobnie by się tego spodziewał.źródło