Map et objet: conversion avec objets imbriqués
Quand on veut sauver le contenu d'une Map de JavaScript dans un fichier, il faut la transformer en chaîne de caractère. Cela serait très simple avec la méthode JSON.stringify(), mais celle-ci ne fonctionne pas sur les nouvelles structures JavaScript comme Map, Set, etc... Cela fonctionne cependant sur une structure classique comme Array ou un objet. Donc avec une étape addditionnelle, transformer Map en objet, la persistence de Map devient en fait facile.
Pour récupérer les données dans le fichier, on utilisera JSON.parse() pour convertir la chaîne de caractère en objet avant de convertir cet objet en Map.
La fonction entries() de Object, produit une Map à partir d'un object, donc la conversion pourrait donc se faire avec une seule commande:
var m = new Map(Object.entries(o))
Mais on va voir plus loin que c'est un peu plus compliqué que cela.
Convertir Map en object
Essayons d'abord avec cette fonction simple:
var o = {}
var m = Map([
[ "a", "one"],
[ "b": "two"],
[ "c": "three"],
[ "d": new Map([ [ "e": "four"] ]) ]
])
function mapToObject(o, m) {
for(let[k,v] of m) { o[k] = v }
}
console.log(JSON.stringify(mapToObject(o, m), null, ' '))
Map est bien devenu un objet, mais nous avons un problème, la structure Map imbriquée associée à la clé "d" n'est pas convertie. Pour traiter les Map imbriquées, nous avons défini une fonction récursive...
function mapToObjectRec(m) {
let lo = {}
for(let[k,v] of m) {
if(v instanceof Map) {
lo[k] = mapToObjectRec(v)
}
else {
lo[k] = v
}
}
return lo
}
var o2 = mapToObjectRec(m)
console.log(JSON.stringify(mapToObject(o2, m), null, ' '))
La Map associée à la clé "d" est bien convertie en objet et donc nous pouvons sauvegarder tout contenu, par exemple avec cette commande de Node.js:
fs.writeFileSync(filename, JSON.stringify(o2, null, ' '));
Convertir un objet en Map
Pour récupérer les données dans le fichier, la conversion inverse doit être faite.
Même si comme on l'a vu il est possible de faire cette conversion avec une seule instruction quand l'objet ne contient que des valeurs primitives, on rencontre le même problème que précédemment avec les objets imbriqués: ils ne sont pas convertis en structures Map, ils sont directement associés au clés. On obtiendrait après conversion le résultat suivant:
var m = new Map([
[ "a", "one"],
[ "b", "two"],
[ "c", "three"],
[ "d", [Object]]
])
Ce qui n'est généralement pas ce que l'on désire.
Une fonction récursive s'avère ici aussi nécessaire pour prendre en compte les objets imbriqués que nous avons définie ci-dessous.
var data = fs.readFileSync(fname,'UTF-8')
let o3 = JSON.parse(data)
// On aura l'objet suivant:
{
"a": "one",
"b": "two",
"c": "three",
"d": { "e": "four"}
}
function objectToMap(o) {
let m = new Map()
for(let k of Object.keys(o)) {
if(o[k] instanceof Object) {
m.set(k, objectToMap(o[k]))
}
else {
m.set(k, o[k])
}
}
return m
}
var m2 = objectToMap(o3)
console.log(m2.get("d").get("e"))
Dans cet exemple, on lit la valeur associée à la clé "e" de la Map imbriquée, laquelle est associée à la clé "d", et cela affiche "four" qui nous prouve que l'on a bien reconstitué la structure Map en entier.