Как сохранить карту ES6 в localstorage (или в другом месте)?



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


к сожалению JSON.stringify(a); просто возвращает ' {}', что означает, что a становится пустым объектом при восстановлении.



нашел es6-mapify это позволяет вверх / вниз-кастинг между картой и простым объектом, так что это может быть одно решение, но я надеялся, что мне нужно будет прибегнуть к внешней зависимости просто сохранить мою карту.

657   7  

7 ответов:

предполагая, что и ваши ключи, и ваши значения сериализуемы,

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

должны работать. Для обратного используйте

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

обычно сериализация полезна только в том случае, если это свойство содержит

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

здесь a ≈ b можно определить как serialize(a) === serialize(b).

это выполняется при сериализации объекта в 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 :)

и это работает, потому что свойства могут быть только строками, которые могут быть без потерь сериализованы в строки.

однако карты ES6 допускают произвольные значения в качестве ключей. Это проблематично, потому что объекты однозначно идентифицируются по их ссылке, а не их данные. И при сериализации объектов вы теряете ссылки.

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 :(

так я бы сказал вообще это невозможно чтобы сделать это полезным способом.

и для тех случаев, когда она будет работать, скорее всего вы можете использовать простой объект вместо карты. Это также будет иметь следующие преимущества:

  • он сможет быть привязан к JSON без потери ключевой информации.
  • он будет работать на старых броузеры.
  • это может быть быстрее.

строительство - ля -ответ, мы можем сделать немного лучше. Мы все еще можем использовать ссылки на объекты для ключей, пока есть примитивный корень или вход в карту, и каждый ключ объекта может быть транзитивно найден из этого корневого ключа.

изменение примера Ориола для использования Дугласа Крокфорда JSON.decycle и JSON.ретроцикл мы можем создать карту, которая обрабатывает этот случай:

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 и retrocycle делают его можно кодировать циклических структур и групп обеспечения доступности баз данных в JSON. Это полезно, если мы хотим построить отношения между объектами без создания дополнительных свойств на самих этих объектах или хотим взаимозаменяемо связать примитивы с объектами и наоборот, используя карту ES6.

одна ловушка в том, что мы не может используйте исходный ключевой объект для новой карты (map3.get(key); вернет undefined). Однако, удерживая исходную ссылку на ключ, но недавно проанализированный JSON карта кажется очень маловероятным случаем, чтобы когда-либо иметь.

чистый, как свисток:

JSON.stringify([...myMap])

принятый ответ не будет выполнен, когда у вас есть многомерные карты. Следует всегда иметь в виду, что объект карты может принимать другой объект карты в качестве ключа или значения.

таким образом, лучший и безопасный способ обработки этой работы может быть следующим;

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

Если у вас есть этот инструмент, то вы можете всегда нравится;

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

одна вещь, которая остается вне этой карты-это приказал структура-т. е. при итерации первый введенный элемент будет первым в списке.

Это не как объект Javascript. Мне потребовался этот тип структуры (поэтому я использовал Map), а затем, чтобы узнать, что JSON.stringify не работает болезненно (но понятно).

Я закончил тем, что сделал функцию 'value_to_json', что означает разбор всего - используя JSON.преобразовать в строки только для самых основных "типов".

к сожалению, подкласс карты с a .toJSON () не работает, так как он исключает значение не JSON_string. И считается наследием.

мой вариант использования был бы исключительным, хотя.

по теме:

function value_to_json(value) {
  if (value === null) {
    return 'null';
  }
  if (value === undefined) {
    return 'null';
  }
  //DEAL WITH +/- INF at your leisure - null instead..

  const type = typeof value;
  //handle as much as possible taht have no side effects. function could
  //return some MAP / SET -> TODO, but not likely
  if (['string', 'boolean', 'number', 'function'].includes(type)) {
    return JSON.stringify(value)
  } else if (Object.prototype.toString.call(value) === '[object Object]') {
    let parts = [];
    for (let key in value) {
      if (Object.prototype.hasOwnProperty.call(value, key)) {
        parts.push(JSON.stringify(key) + ': ' + value_to_json(value[key]));
      }
    }
    return '{' + parts.join(',') + '}';
  }
  else if (value instanceof Map) {
    let parts_in_order = [];
    value.forEach((entry, key) => {
      if (typeof key === 'string') {
        parts_in_order.push(JSON.stringify(key) + ':' + value_to_json(entry));
      } else {
        console.log('Non String KEYS in MAP not directly supported');
      }
      //FOR OTHER KEY TYPES ADD CUSTOM... 'Key' encoding...
    });
    return '{' + parts_in_order.join(',') + '}';
  } else if (typeof value[Symbol.iterator] !== "undefined") {
    //Other iterables like SET (also in ORDER)
    let parts = [];
    for (let entry of value) {
      parts.push(value_to_json(entry))
    }
    return '[' + parts.join(',') + ']';
  } else {
    return JSON.stringify(value)
  }
}


let m = new Map();
m.set('first', 'first_value');
m.set('second', 'second_value');
let m2 = new Map();
m2.set('nested', 'nested_value');
m.set('sub_map', m2);
let map_in_array = new Map();
map_in_array.set('key', 'value');
let set1 = new Set(["1", 2, 3.0, 4]);

m2.set('array_here', [map_in_array, "Hello", true, 0.1, null, undefined, Number.POSITIVE_INFINITY, {
  "a": 4
}]);
m2.set('a set: ', set1);
const test = {
  "hello": "ok",
  "map": m
};

console.log(value_to_json(test));

если вы реализуете свой собственный toJSON() функции для любого class объекты у вас тогда обычный старый JSON.stringify() будет просто работать!

MapС Array s для ключей? MapС другой Map как ценности? А Map дома Object? Может быть, даже свой собственный класс; легко.

Map.prototype.toJSON = function() {
    return Array.from(this.entries());
};

вот именно! здесь требуется манипуляция прототипом. Вы можете пойти вокруг добавления toJSON() вручную для всех ваших нестандартные вещи, но на самом деле вы просто избегаете власти JS

демо


test = {
    regular : 'object',
    map     : new Map([
        [['array', 'key'], 7],
        ['stringKey'     , new Map([
            ['innerMap'    , 'supported'],
            ['anotherValue', 8]
        ])]
    ])
};
console.log(JSON.stringify(test));

выходы:

{"regular":"object","map":[[["array","key"],7],["stringKey",[["innerMap","supported"],["anotherValue",8]]]]}

десериализация не является автоматической, но так же просто, используя приведенную выше результирующую строку, я переделаю Карты, чтобы вытащить значение:

test2 = JSON.parse(JSON.stringify(test));
console.log((new Map((new Map(test2.map)).get('stringKey'))).get('innerMap'));

очевидно, что вы не сделаете это в одной строке и не пренебрегаете сохранением промежуточных карт, это просто докажет, что все данные там, чтобы восстановить оригинал, чтобы использовать get()

"supported"

Comments

    Ничего не найдено.