¿Cómo convertir un objeto plano en un mapa ES6?

128

Por alguna razón, no puedo encontrar esta cosa simple en los documentos de MDN (tal vez me lo pierda).

Esperaba que esto funcionara:

const map = new Map({foo: 'bar'});

map.get('foo'); // 'bar'

... pero la primera línea arroja TypeError: (var)[Symbol.iterator] is not a function

¿Cómo hago un mapa a partir de un objeto plano? ¿Realmente tengo que convertirlo primero en una matriz de matrices de pares clave-valor?

callum
fuente
2
FWIW, puede valer la pena cambiar su respuesta aceptada de la mía a la de nils o bergi . Object.entriesrealmente es el mejor enfoque Object.keys, y el enfoque de función del generador de bergi es un poco más directo que Object.keyso Object.entries.
TJ Crowder

Respuestas:

195

Sí, el Mapconstructor toma una matriz de pares clave-valor.

Object.entrieses un nuevo método estático de objetos disponible en ES2017 (19.1.2.5) .

const map = new Map(Object.entries({foo: 'bar'}));

map.get('foo'); // 'bar'

Actualmente está implementado en Firefox 46+ y Edge 14+ y versiones más recientes de Chrome

Si necesita admitir entornos más antiguos y la transpilación no es una opción para usted, use un polyfill, como el recomendado por georg:

Object.entries = typeof Object.entries === 'function' ? Object.entries : obj => Object.keys(obj).map(k => [k, obj[k]]);
nils
fuente
3
Un "polyfill" será bastante trivial:Object.entries = obj => Object.keys(obj).map(k => [k, obj[k]])
georg
4
Object.entriesaterrizó en el Nodo 7.x propiamente dicho (sin bandera) por cierto
Lee Benson
2
Object.entries no solo está en Node7, sino que también es parte de la especificación final de ES2017. Por lo tanto, esta debería ser la respuesta aceptada, IMO
AnilRedshift
1
@AnilRedshift - Bueno, esto o la función del generador responden , ya que el OP pidió una solución para evitar intermediarios.
TJ Crowder
2
Desafortunadamente, no lo hará.
Nemanja Milosavljevic
78

Vea la respuesta de nils usandoObject.entries y / o la respuesta de bergi usando una función generadora . Aunque aún Object.entriesno estaba en la especificación cuando se hizo la pregunta, estaba en la Etapa 4 , por lo que es seguro para rellenar y usar incluso en abril de 2016 (solo). (Más sobre las etapas aquí .) Y las funciones del generador estaban en ES2015. El OP pidió específicamente evitar intermediarios, y aunque el generador no lo evita por completo, hace un mejor trabajo que el siguiente o (ligeramente) Object.enties.

FWIW, usando Object.entries:

  • Crea una matriz de [name, value]matrices para pasar anew Map
  • El Mapconstructor llama a una función en la matriz para obtener un iterador; la matriz crea y devuelve un objeto interador de matriz.
  • El Mapconstructor usa ese objeto iterador para obtener las entradas (las [name, value]matrices) y construir el mapa

Usando el generador:

  • Crea un objeto generador como resultado de llamar a la función del generador
  • El Mapconstructor llama a una función en ese objeto generador para obtener un iterador de él; el objeto generador se devuelve a sí mismo
  • El Mapconstructor usa el objeto generador (como iterador) para obtener las entradas (las [name, value]matrices) y construir el mapa

Entonces: Un intermediario menos (la matriz de Object.entries).

Sin embargo, usar Object.entrieses más simple y crear esa matriz no es un problema el 99,999% de las veces. Entonces realmente, cualquiera. Pero ambos son mejores que los siguientes. :-)


Respuesta original:

Para inicializar a Map, puede utilizar cualquier iterador que devuelva pares clave / valor como matrices, como una matriz de matrices:

const map = new Map([
    ['foo', 'bar']
]);

No hay conversión incorporada de objeto a mapa, pero se hace fácilmente con Object.keys:

const map = new Map();
let obj = {foo: 'bar'};
Object.keys(obj).forEach(key => {
    map.set(key, obj[key]);
});

Por supuesto, puedes darte una función de trabajador para manejar eso:

function buildMap(obj) {
    let map = new Map();
    Object.keys(obj).forEach(key => {
        map.set(key, obj[key]);
    });
    return map;
}

Luego

const map = buildMap({foo: 'bar'});

O aquí hay una versión más l33t (¿sigue siendo una cosa?):

function buildMap(obj) {
    return Object.keys(obj).reduce((map, key) => map.set(key, obj[key]), new Map());
}

(Sí, Map#setdevuelve la referencia del mapa. Algunos dirían que se trata de un abuso de reduce).

O realmente podemos exagerar en la oscuridad:

const buildMap = o => Object.keys(o).reduce((m, k) => m.set(k, o[k]), new Map());

No, nunca haría eso de verdad. :-)

TJ Crowder
fuente
1
La fusión de Ohar @ de @ y soluciones de TJCrowder: var buildMap2 = o => new Map(Object.keys(o).map(k => [k, o[k]]));.
7vujy0f0hy
17
Ver new Map(Object.entries(object)) stackoverflow.com/a/36644558/798133
Rafael Xavier
las respuestas Object.entries deben ser preferidas
Kir
@Kir - Eso o el generador , sí. Ver mi edición.
TJ Crowder
31

¿Realmente tengo que convertirlo primero en una matriz de matrices de pares clave-valor?

No, un iterador de matrices de pares clave-valor es suficiente. Puede utilizar lo siguiente para evitar crear la matriz intermedia:

function* entries(obj) {
    for (let key in obj)
        yield [key, obj[key]];
}

const map = new Map(entries({foo: 'bar'}));
map.get('foo'); // 'bar'
Bergi
fuente
Buen ejemplo: solo una nota para los demás, es posible que desee hacer un if(!obj.hasOwnProperties(key)) continue;derecho después de la condición de bucle for para asegurarse de no ceder propiedades heredadas del prototipo del objeto (a menos que confíe en el objeto, pero debería hacerlo de todos modos al iterar objetos usando incomo un buen hábito).
Puiu
2
@Puiu No, no deberías, y es un mal hábito. Si no confía en el objeto, tampoco debe confiar en su .hasOwnPropertypropiedad, y tendría que usarloObject.prototype.hasOwnProperty.call(obj, key)
Bergi
2
Sí, creo que no molestarse es la mejor práctica. Debe confiar en que todos los objetos que vale la pena enumerar (es decir, mapas clave-valor) no tengan propiedades heredadas enumerables. (Y sí, por supuesto, evite enumerar matrices ). Si tiene un caso raro de enumerar algo más y se molesta, al menos debería hacerlo correctamente con call. Todas las convenciones que recomiendan obj.hasOwnProperties(key)parecen no tener idea de lo que están haciendo.
Bergi
3
Convertir un Objecten un Mapes una operación costosa y el OP pidió específicamente una solución sin intermediarios. Entonces, ¿por qué no es esta la respuesta exceptuada? Bueno, preguntar esto probablemente sea inútil, simplemente me molesta.
1
@tmvnty Es posible que deba deletrear el tipo function* entries<T>(obj: T): Generator<readonly [keyof T, T[keyof T]]> {…}. También yield [key, obj[key]] as const;ayudaría a TypeScript a darse cuenta de que está produciendo tuplas, no matrices.
Bergi
11

La respuesta de Nils describe cómo convertir objetos en mapas, lo que encontré muy útil. Sin embargo, el OP también se preguntaba dónde está esta información en los documentos de MDN. Si bien es posible que no estuviera allí cuando se hizo la pregunta originalmente, ahora está en la página MDN para Object.entries () bajo el encabezado Convertir un objeto en un mapa que dice:

Convertir un objeto en un mapa

El new Map()constructor acepta un iterable de entries. Con Object.entries, puede convertir fácilmente de Objecta Map:

const obj = { foo: 'bar', baz: 42 }; 
const map = new Map(Object.entries(obj));
console.log(map); // Map { foo: "bar", baz: 42 }
Andrew Willems
fuente
7
const myMap = new Map(
    Object
        .keys(myObj)
        .map(
            key => [key, myObj[key]]
        )
)
Ohar
fuente
1
La fusión de Ohar @ de @ y soluciones de TJCrowder: var buildMap2 = o => new Map(Object.keys(o).map(k => [k, o[k]]));.
7vujy0f0hy
¡Gracias, ya que necesitaba ordenar las claves de objeto también!
scottwernervt
4

Alternativamente, puede usar el método lodash toPairs :

const _ = require('lodash');
const map = new Map(_.toPairs({foo: 'bar'}));
Maurits Rijk
fuente
1

ES6

convertir objeto a mapa:

const objToMap = (o) => new Map(Object.entries(o));

convertir mapa en objeto:

const mapToObj = (m) => [...m].reduce( (o,v)=>{ o[v[0]] = v[1]; return o; },{} )

Nota: la función mapToObj asume que las claves del mapa son cadenas (fallarán de lo contrario)

Yair Levy
fuente
-1

Con la ayuda de JQuery,

const myMap = new Map();
$.each( obj, function( key, value ) {
myMap[key] = value;
});
Patrick Mutuku
fuente
4
En 2019 siento que no deberíamos estar BUSCANDO jquery para resolver problemas.
illcrx
jQuery todavía está en muchos proyectos y esta no es una mala respuesta, simplemente no es la óptima.
Boatcoder