.map () bir Javascript ES6 Haritası?


98

Bunu nasıl yapardın? İçgüdüsel olarak şunu yapmak istiyorum:

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 }

Yeni yineleme protokolüyle ilgili belgelerden pek bir şey öğrenmedim .

Ben farkındayım wu.js , ama çalıştırıyorum Babel projesini değil katmak istiyor musunuz Traceur , şu anda bağlıdır gibi görünüyor .

Ayrıca fitzgen / wu.js'nin bunu kendi projemde nasıl yaptığını nasıl çıkaracağım konusunda biraz bilgim yok .

Burada kaçırdığım şeyin net ve öz bir açıklamasını çok isterim. Teşekkürler!


ES6 Haritası , Bilginize Dokümanlar


Kullanabiliyor Array.frommusun?
Ry-

@minitech Muhtemelen, bir polyfill ile ... bunu olmadan yapmanın bir yolu yok mu?
neezer

mapYinelemelerde kullanmak için kendi işlevinizi kullanarak yazabilirsiniz for of.
Ry-

Süper nitpick, ancak gerçekten bir Harita üzerinde haritalama yapacak olsaydınız, sonunda bir Harita geri alırdınız. Aksi takdirde, önce bir Harita'yı bir Diziye dönüştürür ve bir Harita üzerinde .map () oluşturmazsınız. ISO: dimap (x => [... x], x => new Map (x));
Dtipson

@Ry peki, muhtemelen kendi programlama dilimizi yazabiliriz, ama neden ..? Bu oldukça basit bir şey ve çoğu programlama dilinde onlarca yıldır var.
2018

Yanıtlar:


79

Yani .mapkendisi önemsediğiniz tek bir değer sunar ... Bununla başa çıkmanın birkaç yolu vardır:

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

Unutmayın, bu ikinci örnekte birçok yeni şey kullanıyorum ... ... Array.fromyinelenebilir (kullandığınız her zaman [].slice.call( )artı Kümeler ve Haritalar) ve onu bir diziye dönüştürür ... ... Haritalar , bir diziye zorlandığında, bir dizi dizisine dönüşür, buradael[0] === key && el[1] === value; (temelde, yukarıdaki örnek Haritamı önceden formatta).

Her el için bir nesne döndürmeden önce, bu dizi noktalarını değerlere atamak için lambda'nın bağımsız değişken konumundaki diziyi yok etmeyi kullanıyorum.

Üretimde Babel kullanıyorsanız, Babel'in tarayıcı çoklu dolgusunu ("core-js" ve Facebook'un "rejeneratörü" dahil) kullanmanız gerekecektir.
İçerdiğinden oldukça eminim Array.from.


Evet, bunu Array.fromyapmak için ihtiyacım olacak gibi göründüğünü fark ettim . İlk örneğiniz bir dizi döndürmüyor, btw.
neezer

@neezer hayır, öyle değil. Beklenen kullanımı basit bir yan etkiye dayalı yinelemedir (ES5'ten beri olduğu gibi) , her zaman düz bir şekilde .forEachgeri döner . Bunu kullanmak için , bir dizi oluşturmak ve onu elle doldurmak istersiniz, ya söz konusu ya da ya da tarafından döndürülen yineleyici aracılığıyla . akıl almaz derecede benzersiz bir şey yapmıyor, sadece söz konusu geçişi yapmanın belirli çirkinliğini saklıyor. Ve evet, dahil ederek (veya her neyse) aldığınız şeyi kontrol edin ...undefinedforEachforEachforEach.entries().keys( ).values( )Array.frombabel-core/browser.js.from
Norguard

4
Cevabımı görün, Array.frombir harita işlevini parametre olarak alır, sadece üzerine haritalamak ve atmak için geçici bir dizi oluşturmanıza gerek yok.
loganfsmyth

Nesnenin neden etrafına parantez sarılı olması gerektiğini açıklayabilir misiniz? ES6'da hala biraz yeniyim ve bu kısmı anlamakta güçlük çekiyorum. developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… bir etiket olarak yorumlandığını ve bunun ne anlama geldiğinden emin olmadığımı söylüyor
dtc

1
@dtc evet, yazdığınız bir nesneyi yazdığınızda const obj = { x: 1 };, bir kod bloğu yazdığınızda if (...) { /* block */ }, yazarsınız , bir ok işlevi yazdığınızda yazarsınız () => { return 1; }, peki bir nesnenin parantezine karşı bir işlev gövdesi olduğunu nasıl anlar? Ve cevap parantez. Aksi takdirde, adın nadiren kullanılan bir özellik kod bloğu için bir etiket olmasını bekler, ancak switchbir döngüyü veya bir döngüyü etiketleyebilirsiniz , böylece break;bunlardan adıyla çıkabilirsiniz . Yani eksikse, bir etiket içeren bir işlev gövdeniz 1ve MDN örneğine dayanan
ifadeniz var

35

Yalnızca Yayma operatörünü kullanmalısınız :

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]


Hâlâ gerektiriyor gibi görünüyor Array.from, FYI.
neezer

1
@neezer , sayfanızda Babel-transpiled kodunu üretimde kullanmak için kesinlikle Babel'in polyfill'ini mutlaka kullanmalısınız. Tüm bu yöntemleri (Facebook'un Regenerator dahil) tek başınıza, elle uygulamadığınız sürece, bunun etrafından
dolaşmanın bir yolu yok

@Norguard Anladım - sadece yapmam gereken bir yapılandırma değişikliği demek istediğim. Daha çok bir kenara, gerçekten. :)
neezer

5
Sadece not [...myMap].map(mapFn)etmek gerekirse, geçici bir dizi oluşturacak ve sonra onu atacak. Array.froma'yı mapFnikinci argüman olarak alır , sadece bunu kullanın.
loganfsmyth

2
@wZVanG Neden olmasın Array.from(myMap.values(), val => val + 1);?
loganfsmyth

22

Sadece kullan Array.from(iterable, [mapFn]).

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

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

Bu cevap için teşekkürler, keşke daha yüksek olsaydı. Bir haritayı her zaman geçici bir diziye yayma modelini kullanıyorum, pervasızca farkında olmadan Array.from()bir eşleme işlevini isteğe bağlı bir ikinci parametre olarak aldı.
Andru

Bu çok güzel. Ve çok Akış ile güzel oynuyor, kullanırken Map.values(), aksine Array.values () hala yok: facepalm:
ericsoco

4

Bu işlevi kullanabilirsiniz:

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

kullanım:

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

Kullanarak Array.from, değerleri eşleyen bir Typescript işlevi yazdım:

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

Aslında, Mapile diziye dönüştürdükten sonra orijinal anahtarlarla hala bir a sahip olabilirsiniz Array.from. Bu , ilk öğenin ve ikinci öğenin dönüştürülmüş olduğu bir dizi döndürmekle mümkündür .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 }

Bu anahtarı ilk dizi öğesi olarak döndürmezseniz, anahtarlarınızı kaybedersiniz Map.


1

Haritayı genişletmeyi tercih ederim

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'i ve her döngüde değeri değiştirmek için map.set'i kullanabilirsiniz.

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


Sanırım bu noktayı kaçırıyor. Array.map hiçbir şeyi değiştirmez.
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

Tüm Haritayı önceden bir diziye dönüştürmek ve / veya anahtar-değer dizilerini yok etmek istemiyorsanız, bu saçma işlevi kullanabilirsiniz:

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

Prototipleri düzenlemekten kaçınma konusundaki dini tutkunuza bağlı, ancak bunun sezgisel kalmamı sağladığını düşünüyorum.


0

Dizileri eşleyebilirsiniz, ancak Haritalar için böyle bir işlem yoktur. Dan çözüm Dr Axel Rauschmayer :

  • Haritayı [anahtar, değer] çifti dizisine dönüştürün.
  • Diziyi eşleyin veya filtreleyin.
  • Sonucu tekrar haritaya dönüştürün.

Misal:

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

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

sonuçlandı

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

-1

Belki bu şekilde:

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

Şunlardan Mapönce sadece maymun yama yapmanız gerekir :

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

Bu yamanın daha basit bir biçimini yazabilirdik:

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

Ama o zaman bizi m.map(([k, v]) => [k, v * 2]);biraz daha acı verici ve çirkin görünen yazmaya zorlardık.

Yalnızca eşleme değerleri

Yalnızca değerleri haritalayabilirdik, ancak çok spesifik olduğu için bu çözüme gitmeyi tavsiye etmem. Yine de yapılabilir ve aşağıdaki API'ye sahip oluruz:

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

Tıpkı bu şekilde yama uygulamadan önceki gibi:

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

Belki her ikisine de sahip olabilirsiniz mapValues, nesneyi muhtemelen bekleneceği gibi eşlemediğinizi netleştirmek için ikincisini adlandırabilirsiniz .

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.