Como converter um objeto simples em um mapa ES6?

128

Por algum motivo, não consigo encontrar essa coisa simples nos documentos MDN (talvez eu esteja apenas perdendo).

Eu esperava que isso funcionasse:

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

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

... mas a primeira linha joga TypeError: (var)[Symbol.iterator] is not a function

Como faço um mapa de um objeto simples? Eu realmente preciso primeiro convertê-lo em uma matriz de matrizes de pares de valores-chave?

callum
fonte
2
FWIW, pode valer a pena mudar sua resposta aceita de minha para nils ' ou bergi's . Object.entriesrealmente é a melhor abordagem Object.keys, e a abordagem da função geradora de bergi é ligeiramente mais direta do que Object.keysou Object.entries.
TJ Crowder

Respostas:

194

Sim, o Mapconstrutor usa uma matriz de pares de valores-chave.

Object.entriesé um novo método estático do objeto disponível no ES2017 (19.1.2.5) .

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

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

Atualmente está implementado no Firefox 46+ e Edge 14+ e em versões mais recentes do Chrome

Se você precisar oferecer suporte a ambientes mais antigos e a transpilação não for uma opção para você, use um polyfill, como o recomendado por georg:

Object.entries = typeof Object.entries === 'function' ? Object.entries : obj => Object.keys(obj).map(k => [k, obj[k]]);
nada
fonte
3
Um "polyfill" será bastante trivial:Object.entries = obj => Object.keys(obj).map(k => [k, obj[k]])
georg
4
Object.entriespousou no Nó 7.x propriamente dito (sem bandeira) btw
Lee Benson
2
Object.entries não está apenas no Node7, ele também faz parte da especificação final ES2017. Portanto, esta deve ser a resposta aceita, IMO
AnilRedshift
1
@AnilRedshift - Bem, esta ou a função geradora responde , já que o OP pediu uma solução evitando intermediários.
TJ Crowder
2
Infelizmente, isso não acontecerá.
Nemanja Milosavljevic
78

Por favor, veja a resposta de nils usandoObject.entries e / ou a resposta de bergi usando uma função de gerador . Embora Object.entriesainda não estivesse nas especificações quando a pergunta foi feita, estava no estágio 4 , portanto, seguro para polyfill e uso em abril de 2016 (apenas). (Mais sobre os estágios aqui .) E as funções do gerador estavam no ES2015. O OP pediu especificamente para evitar intermediários e, embora o gerador não evite completamente isso, ele faz um trabalho melhor do que o abaixo ou (ligeiramente) Object.enties.

FWIW, usando Object.entries:

  • Cria uma matriz de [name, value]matrizes para passar paranew Map
  • O Mapconstrutor chama uma função na matriz para obter um iterador; a matriz cria e retorna um objeto interator de matriz.
  • O Mapconstrutor usa esse objeto iterador para obter as entradas (os [name, value]arrays) e construir o mapa

Usando o gerador:

  • Cria um objeto gerador como resultado da chamada da função do gerador
  • O Mapconstrutor chama uma função nesse objeto gerador para obter um iterador dele; o objeto gerador retorna a si mesmo
  • O Mapconstrutor usa o objeto gerador (como um iterador) para obter as entradas (os [name, value]arrays) e construir o mapa

Portanto: Um intermediário a menos (a matriz de Object.entries).

No entanto, usar Object.entriesé mais simples e criar essa matriz não é um problema 99,999% do tempo. Então, realmente, qualquer um. Mas ambos são melhores do que os de baixo. :-)


Resposta original:

Para inicializar um Map, você pode usar qualquer iterador que retorne pares de chave / valor como matrizes, como uma matriz de matrizes:

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

Não há conversão integrada de objeto em mapa, mas é facilmente feita com Object.keys:

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

Você pode, é claro, atribuir a si mesmo uma função de trabalhador para cuidar disso:

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

Então

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

Ou aqui está uma versão mais l33t (isso ainda é uma coisa?):

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

(Sim, Map#setretorna a referência do mapa. Alguns diriam que isso é um abuso de reduce.)

Ou podemos realmente exagerar na obscuridade:

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

Não, eu nunca faria isso de verdade. :-)

TJ Crowder
fonte
1
Fusão de @ Ohar de e @ soluções de TJCrowder: var buildMap2 = o => new Map(Object.keys(o).map(k => [k, o[k]]));.
7vujy0f0hy
17
Consulte new Map(Object.entries(object)) stackoverflow.com/a/36644558/798133
Rafael Xavier
as respostas Object.entries devem ser preferidas
Kir
@Kir - Isso ou o gerador , sim. Veja minha edição.
TJ Crowder
31

Eu realmente preciso primeiro convertê-lo em uma matriz de matrizes de pares de valores-chave?

Não, um iterador de matrizes de pares de valores-chave é suficiente. Você pode usar o seguinte para evitar a criação da matriz intermediária:

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

const map = new Map(entries({foo: 'bar'}));
map.get('foo'); // 'bar'
Bergi
fonte
Bom exemplo - apenas uma observação para os outros, você pode querer fazer um if(!obj.hasOwnProperties(key)) continue;certo após a condição do loop for para garantir que não produza propriedades herdadas do protótipo do objeto (a menos que você confie no objeto, mas deve fazer isso de qualquer maneira ao iterar objetos usando incomo um bom hábito).
puiu
2
@Puiu Não, não devia, e é um péssimo hábito. Se você não confia no objeto, também não deve confiar em sua .hasOwnPropertypropriedade e deverá usáObject.prototype.hasOwnProperty.call(obj, key)
lo
2
Sim, acho que não incomodar é a melhor prática. Você deve confiar que todos os objetos que valem a pena enumerar (ou seja, mapas de valores-chave) não têm propriedades herdadas enumeráveis. (E sim, claro, evite enumerar matrizes ). Se você tiver um caso raro de enumerar algo mais e se incomodar, deve pelo menos fazê-lo corretamente com call. Todas as convenções que recomendam obj.hasOwnProperties(key)parecem não ter ideia do que estão fazendo.
Bergi,
3
Converter um Objectem a Mapé uma operação cara e o OP solicitou especificamente uma solução sem intermediários. Então, por que essa não é a resposta esperada? Bem, perguntar isso provavelmente é fútil, apenas me irrita.
1
@tmvnty Você pode precisar especificar o tipo function* entries<T>(obj: T): Generator<readonly [keyof T, T[keyof T]]> {…}. Também yield [key, obj[key]] as const;ajudaria o TypeScript a perceber que está gerando tuplas, não arrays.
Bergi
11

A resposta de Nils descreve como converter objetos em mapas, o que achei muito útil. No entanto, o OP também estava se perguntando onde essas informações estão nos documentos MDN. Embora possa não estar lá quando a pergunta foi feita originalmente, agora está na página MDN de Object.entries () sob o título Convertendo um Objeto em um Mapa que afirma:

Convertendo um objeto em um mapa

O new Map()construtor aceita um iterável de entries. Com Object.entries, você pode converter facilmente de Objectpara Map:

const obj = { foo: 'bar', baz: 42 }; 
const map = new Map(Object.entries(obj));
console.log(map); // Map { foo: "bar", baz: 42 }
Andrew Willems
fonte
7
const myMap = new Map(
    Object
        .keys(myObj)
        .map(
            key => [key, myObj[key]]
        )
)
Ohar
fonte
1
Fusão de @ Ohar de e @ soluções de TJCrowder: var buildMap2 = o => new Map(Object.keys(o).map(k => [k, o[k]]));.
7vujy0f0hy
Obrigado, pois eu precisava classificar as chaves do objeto também!
scottwernervt
4

Como alternativa, você pode usar o método Lodash toPairs :

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

ES6

converter objeto em mapa:

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

converter mapa em objeto:

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

Observação: a função mapToObj assume que as teclas do mapa são strings (caso contrário, falhará)

Yair Levy
fonte
-1

Com a ajuda de um pouco de JQuery,

const myMap = new Map();
$.each( obj, function( key, value ) {
myMap[key] = value;
});
Patrick Mutuku
fonte
4
Em 2019, sinto que não devemos PROCURAR o jquery para resolver problemas.
Illcrx
jQuery ainda está em muitos projetos e esta não é uma resposta ruim, apenas não é a ideal.
boatcoder