Comment convertir un objet simple en une carte ES6?

128

Pour une raison quelconque, je ne trouve pas cette chose simple dans la documentation MDN (peut-être que je la manque).

Je m'attendais à ce que cela fonctionne:

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

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

... mais la première ligne jette TypeError: (var)[Symbol.iterator] is not a function

Comment créer une carte à partir d'un objet ordinaire? Dois-je vraiment d'abord le convertir en un tableau de tableaux de paires clé-valeur?

callum
la source
2
FWIW, cela peut valoir la peine de changer votre réponse acceptée de la mienne à nils ou bergi . Object.entriesest vraiment la meilleure approche Object.keys, et l'approche de la fonction de générateur de bergi est légèrement plus directe que soit Object.keysou Object.entries.
TJ Crowder

Réponses:

195

Oui, le Mapconstructeur prend un tableau de paires clé-valeur.

Object.entriesest une nouvelle méthode statique d'objets disponible dans ES2017 (19.1.2.5) .

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

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

Il est actuellement implémenté dans Firefox 46+ et Edge 14+ et les versions plus récentes de Chrome

Si vous avez besoin de prendre en charge des environnements plus anciens et que la transpilation n'est pas une option pour vous, utilisez un polyfill, tel que celui recommandé par georg:

Object.entries = typeof Object.entries === 'function' ? Object.entries : obj => Object.keys(obj).map(k => [k, obj[k]]);
nils
la source
3
Un "polyfill" sera plutôt trivial:Object.entries = obj => Object.keys(obj).map(k => [k, obj[k]])
georg
4
Object.entriesa atterri dans Node 7.x proprement dit (sans drapeau) btw
Lee Benson
2
Non seulement Object.entries est dans Node7, mais il fait également partie de la spécification finale ES2017. Ainsi, cela devrait être la réponse acceptée, IMO
AnilRedshift
1
@AnilRedshift - Eh bien, ceci ou la réponse de la fonction générateur , puisque l'OP a demandé une solution évitant les intermédiaires.
TJ Crowder
2
Malheureusement, ce ne sera pas le cas.
Nemanja Milosavljevic
78

Veuillez voir la réponse de nils en utilisantObject.entries et / ou la réponse de bergi en utilisant une fonction de générateur . Bien que Object.entriesn'étant pas encore dans les spécifications lorsque la question a été posée, elle était à l'étape 4 , donc sûr à polyfill et à utiliser même en avril 2016 (juste). (Plus d'informations sur les étapes ici .) Et les fonctions du générateur étaient dans ES2015. L'OP a spécifiquement demandé d'éviter les intermédiaires, et bien que le générateur n'évite pas complètement cela, il fait un meilleur travail que le ci-dessous ou (légèrement) Object.enties.

FWIW, en utilisant Object.entries:

  • Crée un tableau de [name, value]tableaux à passernew Map
  • Le Mapconstructeur appelle une fonction sur le tableau pour obtenir un itérateur; le tableau crée et renvoie un objet interateur de tableau.
  • Le Mapconstructeur utilise cet objet itérateur pour obtenir les entrées (les [name, value]tableaux) et construire la carte

Utilisation du générateur:

  • Crée un objet générateur à la suite de l'appel de la fonction générateur
  • Le Mapconstructeur appelle une fonction sur cet objet générateur pour en obtenir un itérateur; le rendement de l' objet générateur lui - même
  • Le Mapconstructeur utilise l'objet générateur (comme itérateur) pour récupérer les entrées (les [name, value]tableaux) et construire la carte

Donc: un intermédiaire en moins (le tableau de Object.entries).

Cependant, l'utilisation Object.entriesest plus simple et la création de ce tableau n'est pas un problème 99,999% du temps. Alors vraiment, l'un ou l'autre. Mais ils sont tous les deux meilleurs que ceux ci-dessous. :-)


Réponse originale:

Pour initialiser a Map, vous pouvez utiliser n'importe quel itérateur qui renvoie des paires clé / valeur sous forme de tableaux, comme un tableau de tableaux:

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

Il n'y a pas de conversion intégrée d'objet en carte, mais c'est facile à faire avec Object.keys:

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

Vous pouvez, bien sûr, vous donner une fonction de travailleur pour gérer cela:

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

ensuite

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

Ou voici une version plus l33t (est-ce toujours une chose?):

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

(Oui, Map#setrenvoie la référence de la carte. Certains diront qu'il s'agit d'un abus de reduce.)

Ou nous pouvons vraiment exagérer sur l'obscurité:

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

Non, je ne ferais jamais ça pour de vrai. :-)

TJ Crowder
la source
1
Fusion de son @ Zohar et @ solutions de TJCrowder: var buildMap2 = o => new Map(Object.keys(o).map(k => [k, o[k]]));.
7vujy0f0hy
17
Voir new Map(Object.entries(object)) stackoverflow.com/a/36644558/798133
Rafael Xavier
les réponses Object.entries devraient être préférées
Kir
@Kir - Ça ou le générateur , ouais. Voir ma modification.
TJ Crowder du
31

Dois-je vraiment d'abord le convertir en un tableau de tableaux de paires clé-valeur?

Non, un itérateur de tableaux de paires clé-valeur suffit. Vous pouvez utiliser ce qui suit pour éviter de créer le tableau intermédiaire:

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

const map = new Map(entries({foo: 'bar'}));
map.get('foo'); // 'bar'
Bergi
la source
Bel exemple - juste une note pour les autres, vous voudrez peut-être faire un if(!obj.hasOwnProperties(key)) continue;juste après la condition de boucle for pour vous assurer que vous ne donnez pas les propriétés héritées du prototype d'objet (à moins que vous ne fassiez confiance à l'objet, mais que vous devriez le faire quand même lors de l'itération d'objets en utilisant incomme une bonne habitude).
puiu
2
@Puiu Non, vous ne devriez pas, et c'est une mauvaise habitude. Si vous ne faites pas confiance à l'objet, vous ne devez pas non plus faire confiance à sa .hasOwnPropertypropriété et vous devrez utiliserObject.prototype.hasOwnProperty.call(obj, key)
Bergi
2
Oui, je pense que ne pas déranger est la meilleure pratique. Vous devez faire confiance à tous les objets qui valent la peine d'être énumérés (c'est-à-dire les cartes clé-valeur) pour ne pas avoir de propriétés héritées énumérables. (Et oui, évitez bien sûr d' énumérer les tableaux ). Si vous avez un cas rare d'énumération d'autre chose et que vous vous embêtez, vous devriez au moins le faire correctement avec call. Toutes les conventions qui recommandent obj.hasOwnProperties(key)semblent n'avoir aucune idée de ce qu'elles font.
Bergi
3
La conversion d'un Objecten un Mapest une opération coûteuse et l'OP a spécifiquement demandé une solution sans intermédiaires. Alors pourquoi n'est-ce pas la réponse exceptée? Eh bien, demander ceci est probablement futile, cela m'énerve.
1
@tmvnty Vous devrez peut-être préciser le type function* entries<T>(obj: T): Generator<readonly [keyof T, T[keyof T]]> {…}. Aussi yield [key, obj[key]] as const;aiderait tapuscrit se rendre compte qu'il est tuples rendement, pas de tableaux.
Bergi
11

La réponse de Nils décrit comment convertir des objets en cartes, ce que j'ai trouvé très utile. Cependant, l'OP se demandait également où se trouvaient ces informations dans la documentation MDN. Bien qu'il ne soit peut-être pas là lorsque la question a été initialement posée, elle se trouve maintenant sur la page MDN pour Object.entries () sous l'en-tête Conversion d'un objet en carte qui indique:

Conversion d'un objet en carte

Le new Map()constructeur accepte un itérable de entries. Avec Object.entries, vous pouvez facilement convertir de Objecten Map:

const obj = { foo: 'bar', baz: 42 }; 
const map = new Map(Object.entries(obj));
console.log(map); // Map { foo: "bar", baz: 42 }
Andrew Willems
la source
7
const myMap = new Map(
    Object
        .keys(myObj)
        .map(
            key => [key, myObj[key]]
        )
)
Ohar
la source
1
Fusion de son @ Zohar et @ solutions de TJCrowder: var buildMap2 = o => new Map(Object.keys(o).map(k => [k, o[k]]));.
7vujy0f0hy
Merci, car j'avais besoin de trier les clés d'objet aussi!
scottwernervt
4

Vous pouvez également utiliser la méthode lodash toPairs :

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

ES6

convertir un objet en carte:

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

convertir la carte en objet:

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

Remarque: la fonction mapToObj suppose que les clés de mappage sont des chaînes (échoueront sinon)

Yair Levy
la source
-1

Avec l'aide de quelques JQuery,

const myMap = new Map();
$.each( obj, function( key, value ) {
myMap[key] = value;
});
Patrick Mutuku
la source
4
En 2019, je pense que nous ne devrions pas chercher à résoudre des problèmes.
illcrx
jQuery est encore dans de nombreux projets et ce n'est pas une mauvaise réponse, mais pas optimale.
boatcoder