Comment puis-je persister une carte ES6 dans localstorage (ou ailleurs)?

var a = new Map([[ 'a', 1 ]]); a.get('a') // 1 var forStorageSomewhere = JSON.stringify(a); // Store, in my case, in localStorage. // Later: var a = JSON.parse(forStorageSomewhere); a.get('a') // TypeError: undefined is not a function 

Malheureusement JSON.stringify(a); Renvoie simplement '{}', ce qui signifie qu'un devient un objet vide lorsqu'il est restauré.

J'ai trouvé es6-mapify qui permet la mise en place / descendante entre une carte et un objet ordinaire, de sorte que ce pourrait être une solution, mais j'espérais que j'aurais besoin de recourir à une dépendance externe simplement pour persister ma carte.

En supposant que vos clés et vos valeurs sont sérialisables,

 localStorage.myMap = JSON.stringify(Array.from(map.entries())); 

devrait marcher. Pour l'inverse, utilisez

 map = new Map(JSON.parse(localStorage.myMap)); 

Habituellement, la sérialisation n'est utile que si cette propriété est valable

 deserialize(serialize(data)).get(key) ≈ data.get(key) 

a ≈ b pourrait être défini comme serialize(a) === serialize(b) .

Ceci est satisfait lors de la sérialisation d'un objet sur JSON:

 var obj1 = {foo: [1,2]}, obj2 = JSON.parse(JSON.stringify(obj1)); obj1.foo; // [1,2] obj2.foo; // [1,2] :) JSON.stringify(obj1.foo) === JSON.stringify(obj2.foo); // true :) 

Et cela fonctionne parce que les propriétés ne peuvent être que des chaînes, qui peuvent être sérialisées sans perte dans les chaînes.

Cependant, les cartes ES6 permettent des valeurs arbitraires en tant que clés. Ceci est problématique car les objets sont identifiés de manière unique par leur référence, et non par leurs données. Et lors de la sérialisation d'objets, vous perdez les références.

 var key = {}, map1 = new Map([ [1,2], [key,3] ]), map2 = new Map(JSON.parse(JSON.stringify([...map1.entries()]))); map1.get(1); // 2 map2.get(1); // 2 :) map1.get(key); // 3 map2.get(key); // undefined :( 

Donc, je dirais en général qu'il n'est pas possible de le faire de manière utile.

Et pour les cas où cela fonctionnerait, vous pouvez probablement utiliser un objet simple au lieu d'une carte . Cela aura également ces avantages:

  • Il pourra être stringé à JSON sans perdre les informations clés.
  • Cela fonctionnera sur les anciens navigateurs.
  • Il pourrait être plus rapide.

S'appuyant sur la réponse d' Oriol , nous pouvons faire un peu mieux. Nous pouvons toujours utiliser les références d'objet pour les clés tant qu'il existe une racine ou une entrée primitive dans la carte, et chaque clé d'objet peut être trouvée de façon transitoire à partir de cette clé racine.

En modifiant l'exemple d'Oriol pour utiliser JSON.decycle de Douglas Crockford et JSON.retrocycle, nous pouvons créer une carte qui gère cette affaire:

 var key = {}, map1 = new Map([ [1, key], [key, 3] ]), map2 = new Map(JSON.parse(JSON.stringify([...map1.entries()]))), map3 = new Map(JSON.retrocycle(JSON.parse(JSON.stringify(JSON.decycle([...map1.entries()]))))); map1.get(1); // key map2.get(1); // key map3.get(1); // key map1.get(map1.get(1)); // 3 :) map2.get(map2.get(1)); // undefined :( map3.get(map3.get(1)); // 3 :) 

Decycle et retrocycle permettent d'encoder des structures cycliques et des dags dans JSON. Ceci est utile si nous voulons créer des relations entre objets sans créer de propriétés supplémentaires sur ces objets eux-mêmes, ou vouloir interconnecter les primitives aux objets et au visa-versa, en utilisant une carte ES6.

Le seul piège est que nous ne pouvons pas utiliser l'objet clé d'origine pour la nouvelle carte ( map3.get(key); renverra indéfiniment). Toutefois, en tenant la référence de la clé d'origine, une carte JSON nouvellement analysée semble être un cas très peu probable.

La réponse acceptée échouera lorsque vous disposez de cartes multidimensionnelles. Il faut toujours garder à l'esprit que, un objet Map peut prendre un autre objet Map comme clé ou valeur.

Donc, une manière meilleure et plus sûre de gérer ce travail pourrait être la suivante;

 function arrayifyMap(m){ return m.constructor === Map ? [...m].map(([v,k]) => [arrayifyMap(v),arrayifyMap(k)]) : m; } 

Une fois que vous avez cet outil, vous pouvez toujours faire comme;

 localStorage.myMap = JSON.stringify(arrayifyMap(myMap))