Come convertire un semplice oggetto in una mappa ES6?


128

Per qualche motivo non riesco a trovare questa semplice cosa nei documenti MDN (forse mi manca solo).

Mi aspettavo che funzionasse:

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

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

... ma la prima riga lancia TypeError: (var)[Symbol.iterator] is not a function

Come si crea una mappa da un oggetto semplice? Devo davvero convertirlo prima in un array di array di coppie chiave-valore?


2
FWIW, potrebbe valere la pena cambiare la tua risposta accettata dalla mia a quella di zero o bergi . Object.entriesè davvero l'approccio migliore Object.keys, e l'approccio alla funzione del generatore di bergi è leggermente più diretto di Object.keyso Object.entries.
TJ Crowder

Risposte:


195

Sì, il Mapcostruttore accetta un array di coppie chiave-valore.

Object.entriesè un nuovo metodo statico Object disponibile in ES2017 (19.1.2.5) .

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

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

È attualmente implementato in Firefox 46+ e Edge 14+ e nelle versioni più recenti di Chrome

Se hai bisogno di supportare ambienti più vecchi e il transpilation non è un'opzione per te, usa un polyfill, come quello consigliato da georg:

Object.entries = typeof Object.entries === 'function' ? Object.entries : obj => Object.keys(obj).map(k => [k, obj[k]]);

3
Un "polyfill" sarà piuttosto banale:Object.entries = obj => Object.keys(obj).map(k => [k, obj[k]])
georg

4
Object.entriesatterrato nel nodo 7.x vero e proprio (senza bandiera) btw
Lee Benson

2
Object.entries non è solo in Node7, ma fa anche parte della specifica finale di ES2017. Quindi, questa dovrebbe essere la risposta accettata, IMO
AnilRedshift

1
@AnilRedshift - Bene, questa o la risposta della funzione generatore , poiché l'OP ha chiesto una soluzione che evitasse gli intermediari.
TJ Crowder

2
Purtroppo no.
Nemanja Milosavljevic

78

Si prega di vedere la risposta di nils utilizzandoObject.entries e / o la risposta di bergi utilizzando una funzione generatore . Sebbene Object.entriesnon fosse ancora nelle specifiche quando è stata posta la domanda, era allo stadio 4 , quindi sicuro da usare con polyfill anche nell'aprile 2016 (solo). (Maggiori informazioni sulle fasi qui .) E le funzioni del generatore erano in ES2015. L'OP ha chiesto espressamente di evitare intermediari e, sebbene il generatore non lo eviti completamente, fa un lavoro migliore rispetto a quelli sotto o (leggermente) Object.enties.

FWIW, utilizzando Object.entries:

  • Crea un array di [name, value]array a cui passarenew Map
  • Il Mapcostruttore chiama una funzione sull'array per ottenere un iteratore; l'array crea e restituisce un oggetto interator dell'array.
  • Il Mapcostruttore usa quell'oggetto iteratore per ottenere le voci (gli [name, value]array) e costruire la mappa

Utilizzando il generatore:

  • Crea un oggetto generatore come risultato della chiamata della funzione generatore
  • Il Mapcostruttore chiama una funzione su quell'oggetto generatore per ottenere un iteratore da esso; l'oggetto generatore ritorna da solo
  • Il Mapcostruttore usa l'oggetto generatore (come iteratore) per ottenere le voci (gli [name, value]array) e costruire la mappa

Quindi: un intermediario in meno (l'array da Object.entries).

Tuttavia, l'utilizzo Object.entriesè più semplice e la creazione di quell'array non è un problema il 99,999% delle volte. Quindi davvero, nessuno dei due. Ma sono entrambi migliori dei seguenti. :-)


Risposta originale:

Per inizializzare a Map, puoi utilizzare qualsiasi iteratore che restituisca coppie chiave / valore come array, ad esempio un array di array:

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

Non esiste una conversione incorporata da oggetto a mappa, ma è facilmente realizzabile con Object.keys:

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

Puoi, ovviamente, darti una funzione di lavoro per gestirlo:

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

Poi

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

Oppure ecco una versione più l33t (è ancora una cosa?):

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

(Sì, Map#setrestituisce il riferimento della mappa. Alcuni sostengono che si tratta di un abuso di reduce.)

Oppure possiamo davvero esagerare con l'oscurità:

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

No, non lo farei mai per davvero. :-)


1
Fusione di @ Ohar di e @ soluzioni TJCrowder: var buildMap2 = o => new Map(Object.keys(o).map(k => [k, o[k]]));.
7vujy0f0hy

17
Vedi new Map(Object.entries(object)) stackoverflow.com/a/36644558/798133
Rafael Xavier il

le risposte Object.entries dovrebbero essere preferite
Kir

@Kir - Quello o il generatore , sì. Vedi la mia modifica.
TJ Crowder

31

Devo davvero convertirlo prima in un array di array di coppie chiave-valore?

No, è sufficiente un iteratore di array di coppie chiave-valore. È possibile utilizzare quanto segue per evitare di creare l'array intermedio:

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

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

Bell'esempio: solo una nota per gli altri, potresti voler fare un if(!obj.hasOwnProperties(key)) continue;diritto dopo la condizione del ciclo for per assicurarti di non restituire proprietà ereditate dal prototipo dell'oggetto (a meno che non ti fidi dell'oggetto, ma dovresti farlo comunque quando iterare oggetti usando income una buona abitudine).
puiu

2
@Puiu No, non dovresti, ed è una cattiva abitudine. Se non ti fidi dell'oggetto, non devi fidarti anche della sua .hasOwnPropertyproprietà, e dovresti usareObject.prototype.hasOwnProperty.call(obj, key)
Bergi

2
Sì, penso che non disturbare sia la migliore pratica. Dovresti fidarti di tutti gli oggetti che vale la pena enumerare (cioè le mappe valore-chiave) per non avere alcuna proprietà ereditata enumerabile. (E sì, ovviamente, evita di enumerare gli array ). Se hai un raro caso di enumerare qualcos'altro e ti preoccupi, dovresti almeno farlo correttamente con call. Tutte le convenzioni là fuori che raccomandano obj.hasOwnProperties(key)sembrano non avere idea di cosa stanno facendo.
Bergi

3
Convertire un Objectin a Mapè un'operazione costosa e l'OP ha chiesto espressamente una soluzione senza intermedi. Allora perché non è questa la risposta esclusa? Beh, chiederlo probabilmente è inutile, mi dà fastidio.

1
@tmvnty Potrebbe essere necessario precisare il tipo function* entries<T>(obj: T): Generator<readonly [keyof T, T[keyof T]]> {…}. Inoltre yield [key, obj[key]] as const;aiuterebbe TypeScript a rendersi conto che sta producendo tuple, non array.
Bergi

11

La risposta di Nils descrive come convertire gli oggetti in mappe, cosa che ho trovato molto utile. Tuttavia, l'OP si stava anche chiedendo dove fossero queste informazioni nei documenti MDN. Anche se potrebbe non essere presente quando la domanda è stata inizialmente posta, ora si trova nella pagina MDN per Object.entries () sotto l'intestazione Conversione di un oggetto in una mappa che afferma:

Conversione di un oggetto in una mappa

Il new Map()costruttore accetta un iterabile di entries. Con Object.entries, puoi convertire facilmente da Objecta Map:

const obj = { foo: 'bar', baz: 42 }; 
const map = new Map(Object.entries(obj));
console.log(map); // Map { foo: "bar", baz: 42 }

7
const myMap = new Map(
    Object
        .keys(myObj)
        .map(
            key => [key, myObj[key]]
        )
)

1
Fusione di @ Ohar di e @ soluzioni TJCrowder: var buildMap2 = o => new Map(Object.keys(o).map(k => [k, o[k]]));.
7vujy0f0hy

Grazie, perché anche io avevo bisogno di ordinare le chiavi degli oggetti!
scottwernervt


1

ES6

converti oggetto in mappa:

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

converti mappa in oggetto:

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

Nota: la funzione mapToObj presuppone che le chiavi della mappa siano stringhe (altrimenti fallirà)


-1

Con l'aiuto di alcuni JQuery,

const myMap = new Map();
$.each( obj, function( key, value ) {
myMap[key] = value;
});

4
Nel 2019 sento che non dovremmo cercare jquery per risolvere i problemi.
illcrx

jQuery è ancora in molti progetti e questa non è una cattiva risposta, solo non ottimale.
boatcoder
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.