.map () ¿un mapa de Javascript ES6?

89

¿Cómo harías esto? Instintivamente, quiero hacer:

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 }

No he extraído mucho de la documentación sobre el nuevo protocolo de iteración .

Soy consciente de wu.js , pero estoy ejecutando un proyecto de Babel y no quiero incluir Traceur , de lo que parece que depende actualmente .

También no tengo ni idea de cómo extraer cómo lo hizo fitzgen / wu.js en mi propio proyecto.

Me encantaría una explicación clara y concisa de lo que me estoy perdiendo aquí. ¡Gracias!


Documentos para ES6 Map , FYI

neezer
fuente
¿Puedes usar Array.from?
Ry-
@minitech Posiblemente, con un polyfill ... ¿no hay forma de hacer esto sin él?
neezer
Bueno, podría escribir su propia mapfunción para usar en iterables, usando for of.
Ry-
Super quisquilloso, pero si realmente fueras a trazar un mapa sobre un mapa, obtendrías un mapa al final. De lo contrario, primero está convirtiendo un mapa en una matriz y mapeando sobre una matriz, no .map () haciendo un mapa. Puede mapear fácilmente sobre un mapa utilizando un ISO: dimap (x => [... x], x => new Map (x));
Dtipson
@ Ry bueno, probablemente podamos escribir nuestro propio lenguaje de programación, pero ¿por qué ...? Es algo bastante simple y existe en la mayoría de los lenguajes de programación durante muchas décadas.
ruX

Respuestas:

73

Por .maplo tanto , solo ofrece un valor que le importa ... Dicho esto, hay algunas formas de abordar esto:

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

Tenga en cuenta que estoy usando muchas cosas nuevas en ese segundo ejemplo ... ... Array.fromtoma cualquier iterable (en cualquier momento que use [].slice.call( ), más Conjuntos y Mapas) y lo convierte en una matriz ... ... Mapas , cuando se convierte en una matriz, se convierte en una matriz de matrices, donde el[0] === key && el[1] === value;(básicamente, en el mismo formato con el que rellené previamente mi mapa de ejemplo, arriba).

Estoy usando la desestructuración de la matriz en la posición del argumento de la lambda, para asignar esos puntos de matriz a los valores, antes de devolver un objeto para cada el.

Si está usando Babel, en producción, necesitará usar el polyfill del navegador de Babel (que incluye "core-js" y el "regenerador" de Facebook).
Estoy bastante seguro de que contiene Array.from.

Norguard
fuente
Sí, solo me doy cuenta de que parece que necesitaré Array.frompara hacer esto. Su primer ejemplo no devuelve una matriz, por cierto.
neezer
@neezer no, no lo hace. .forEachdevuelve undefinedcompletamente, todo el tiempo, ya que el uso esperado de forEaches una iteración simple basada en efectos secundarios (igual que ha sido desde ES5). Para usar eso forEach, querrá construir una matriz y completarla manualmente, ya sea a través de dicho forEacho mediante el iterador devuelto por .entries()o .keys( )o .values( ). Array.fromno está haciendo nada increíblemente único, solo está ocultando esa fealdad particular de hacer dicho recorrido. Y sí, verifique que al incluir babel-core/browser.js(o lo que sea) obtiene .from...
Norguard
4
Vea mi respuesta, Array.fromtoma una función de mapa como parámetro, no necesita crear una matriz temporal solo para mapearla y descartarla.
loganfsmyth
¿Puedes explicar por qué el objeto necesita un paréntesis envuelto alrededor? Todavía soy un poco nuevo en ES6 y tengo problemas para entender esa parte. developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… dice que se interpreta como una etiqueta y no estoy seguro de lo que eso significa
dtc
1
@dtc sí, cuando escribes un objeto escribes const obj = { x: 1 };, cuando escribes un bloque de código, escribes if (...) { /* block */ }, cuando escribes una función de flecha escribes () => { return 1; }, entonces, ¿cómo sabe cuándo es un cuerpo de función, en comparación con las llaves de un objeto? Y la respuesta está entre paréntesis. De lo contrario, espera que el nombre sea una etiqueta para un bloque de código, una característica que se usa con poca frecuencia, pero puede etiquetar switchun bucle o un bucle, de modo que pueda break;salir de ellos por nombre. Entonces, si falta, tiene un cuerpo de función, con una etiqueta y la declaración 1, según el ejemplo de MDN
Norguard
34

Solo debe usar el operador 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]

Walter Chapilliquen - wZVanG
fuente
Parece que todavía lo requiere Array.from, para su información.
neezer
1
@neezer, absolutamente, positivamente, debe usar el polyfill de Babel, para poder usar el código transpilado de Babel en su página, en producción. No hay forma de evitarlo, a menos que implemente todos esos métodos (incluido el Regenerador de Facebook) usted mismo, a mano ...
evitarlo
@Norguard Entendido, solo quise decir un cambio de configuración que tendré que hacer. En realidad, es más un aparte. :)
neezer
5
Solo para tener en cuenta, [...myMap].map(mapFn)creará una matriz temporal y luego la descartará. Array.fromtoma a mapFncomo segundo argumento, solo utilícelo.
loganfsmyth
2
@wZVanG ¿Por qué no Array.from(myMap.values(), val => val + 1);?
loganfsmyth
21

Solo usa Array.from(iterable, [mapFn]).

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

var newArr = Array.from(myMap.values(), value => value + 1);
loganfsmyth
fuente
Gracias por esta respuesta, desearía que estuviera más arriba. Utilizo el patrón de extender un mapa en una matriz temporal todo el tiempo, sin saberlo, Array.from()tomé una función de mapeo como un segundo parámetro opcional.
Andru
4

Puede utilizar esta función:

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

uso:

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é
fuente
3

Usando Array.fromescribí una función de TypeScript que mapea los valores:

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
fuente
3

En realidad, todavía puede tener una Mapcon las claves originales después de convertir a una matriz con Array.from. Eso es posible al devolver una matriz , donde el primer elemento es el keyy el segundo es el transformado 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 }

Si no devuelve esa clave como primer elemento de la matriz, perderá las Mapclaves.

mayid
fuente
1

Prefiero extender el mapa

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
fuente
1

Puede usar myMap.forEach y, en cada bucle, usar map.set para cambiar el valor.

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

Hunter Liu
fuente
Creo que esto pierde el sentido. Array.map no cambia nada.
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
fuente
0

Si no desea convertir el mapa completo en una matriz de antemano y / o desestructurar las matrices de valores clave, puede usar esta función tonta:

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

Depende de su fervor religioso para evitar editar prototipos, pero encuentro que esto me permite mantenerlo intuitivo.

Commi
fuente
0

Puede map () matrices, pero no existe tal operación para Maps. La solución del Dr. Axel Rauschmayer :

  • Convierta el mapa en una matriz de pares [clave, valor].
  • Asigne o filtre la matriz.
  • Vuelva a convertir el resultado en un mapa.

Ejemplo:

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

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

resultó en

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

Quizás de esta manera:

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

Solo necesitaría parchear mono Mapantes:

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

Podríamos haber escrito una forma más simple de este parche:

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

Pero nos hubiéramos obligado a luego escribir m.map(([k, v]) => [k, v * 2]); que me parece un poco más doloroso y feo.

Solo valores de mapeo

También podríamos mapear solo valores, pero no recomendaría esa solución ya que es demasiado específica. No obstante se puede hacer y tendríamos la siguiente API:

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

Al igual que antes de parchear de esta manera:

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

Tal vez pueda tener ambos, nombrando el segundo mapValuespara dejar en claro que en realidad no está mapeando el objeto como probablemente se esperaría.

cglacet
fuente