Как преобразовать простой объект в карту ES6?

128

По какой-то причине я не могу найти эту простую вещь в документах MDN (может быть, мне ее просто не хватает).

Я ожидал, что это сработает:

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

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

... но первая строка бросает TypeError: (var)[Symbol.iterator] is not a function

Как создать карту из простого объекта? Мне действительно нужно сначала преобразовать его в массив массивов пар ключ-значение?

Каллум
источник
2
FWIW, возможно, стоит сменить принятый ответ с моего на ноль или берги . Object.entriesдействительно лучший подход Object.keys, и подход функции генератора Берги немного более прямой, чем любой Object.keysили Object.entries.
TJ Crowder

Ответы:

195

Да, Mapконструктор принимает массив пар ключ-значение.

Object.entries- новый статический метод Object, доступный в ES2017 (19.1.2.5) .

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

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

В настоящее время он реализован в Firefox 46+ и Edge 14+ и более новых версиях Chrome.

Если вам нужна поддержка более старых сред, а транспиляция вам не подходит, используйте полифил, такой как рекомендованный georg:

Object.entries = typeof Object.entries === 'function' ? Object.entries : obj => Object.keys(obj).map(k => [k, obj[k]]);
Нильс
источник
3
«Полифилл» будет довольно тривиальным:Object.entries = obj => Object.keys(obj).map(k => [k, obj[k]])
georg
4
Object.entriesприземлился в Node 7.x собственно (без флага), кстати
Ли Бенсон
2
Object.entries не только входит в Node7, но и является частью окончательной спецификации ES2017. Таким образом, это должен быть принятый ответ, ИМО
AnilRedshift
1
@AnilRedshift - Ну, это или ответ функции генератора , так как ОП попросил решение, избегающее посредников.
TJ Crowder
2
К сожалению, этого не произойдет.
Неманья Милосавлевич
78

См. Ответ nils с использованиемObject.entries и / или ответ Bergi с использованием функции генератора . Хотя Object.entriesкогда задавался вопрос, он еще не входил в спецификацию, но он находился на этапе 4 , поэтому его можно было полифиллировать и использовать даже в апреле 2016 года (просто). (Подробнее об этапах здесь .) И функции генератора были в ES2015. OP специально просил избегать посредников, и хотя генератор не полностью избегает этого, он работает лучше, чем ниже или (немного) Object.enties.

FWIW, используя Object.entries:

  • Создает массив [name, value]массивов для передачиnew Map
  • MapКонструктор вызывает функцию на массив , чтобы получить итератор; массив создает и возвращает объект-интегратор массива.
  • В Mapиспользуете конструктора , который итератор объект , чтобы получить записи (в [name, value]массивах) и построить карту

Используя генератор:

  • Создает объект-генератор в результате вызова функции-генератора
  • MapКонструктор вызывает функцию на этом объекте генератора , чтобы получить итератор от него; объект-генератор возвращает себя
  • MapКонструктор использует объект генератора ( в качестве итератора) , чтобы получить записи (в [name, value]массивах) и построить карту

Итак: На одного посредника меньше (массив из Object.entries).

Однако использовать Object.entriesпроще, и создание этого массива не является проблемой в 99,999% случаев. Так что действительно, любой. Но они оба лучше, чем ниже. :-)


Оригинальный ответ:

Для инициализации a Mapвы можете использовать любой итератор, который возвращает пары ключ / значение в виде массивов, например массив массивов:

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

Встроенного преобразования из объекта в карту нет, но это легко сделать с помощью Object.keys:

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

Вы, конечно, можете дать себе рабочую функцию для этого:

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

затем

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

Или вот более l33t-выглядящая (это все еще актуально?) Версия:

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

(Да, Map#setвозвращает ссылку на карту. Некоторые утверждают, что это неправомерное использование reduce.)

Или мы можем действительно перейти на топ на неизвестность:

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

Нет, я бы никогда этого не сделал. :-)

TJ Crowder
источник
1
Слияние @ Ohar - х и @ решений TJCrowder в: var buildMap2 = o => new Map(Object.keys(o).map(k => [k, o[k]]));.
7vujy0f0hy
17
См. new Map(Object.entries(object)) Stackoverflow.com/a/36644558/798133
Рафаэль Ксавье
предпочтение следует отдавать ответам Object.entries
Кир
@ Кир - Это или генератор , да. Смотрите мою правку.
TJ Crowder
31

Мне действительно нужно сначала преобразовать его в массив массивов пар ключ-значение?

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

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

const map = new Map(entries({foo: 'bar'}));
map.get('foo'); // 'bar'
Берги
источник
Хороший пример - просто примечание для других, вы можете захотеть сделать if(!obj.hasOwnProperties(key)) continue;сразу после условия цикла for, чтобы гарантировать, что вы не получите свойства, унаследованные от прототипа объекта (если вы не доверяете объекту, но должны делать это в любом случае при повторении объектов с использованием inкак хорошая привычка).
puiu 02
2
@Puiu Нет, не стоит, это плохая привычка. Если вы не доверяете объекту, вы не должны доверять и его .hasOwnPropertyсобственность, и вам придется использоватьObject.prototype.hasOwnProperty.call(obj, key)
Берги
2
Да, я думаю, что лучше не беспокоиться. Вы должны доверять всем объектам, которые стоит перечислить (то есть картам ключ-значение), чтобы они не имели никаких перечислимых наследуемых свойств. (И да, конечно, избегайте перечисления массивов ). Если у вас есть редкий случай перечисления чего-то еще, и вы беспокоитесь, вы должны, по крайней мере, сделать это правильно с call. obj.hasOwnProperties(key)Кажется, что все рекомендующие соглашения не имеют ни малейшего представления о том, что делают.
Берги
3
Преобразование Objectв a Map- дорогостоящая операция, и OP специально попросил решение без промежуточных продуктов. Так почему же это не исключительный ответ? Что ж, спрашивать об этом, наверное, бесполезно, это меня просто раздражает.
1
@tmvnty Возможно, вам потребуется указать тип function* entries<T>(obj: T): Generator<readonly [keyof T, T[keyof T]]> {…}. Также yield [key, obj[key]] as const;поможет TypeScript понять, что он генерирует кортежи, а не массивы.
Берги
11

Ответ Нильса описывает, как преобразовывать объекты в карты, что мне показалось очень полезным. Однако OP также задавался вопросом, где эта информация находится в документах MDN. Хотя его могло и не быть, когда вопрос был первоначально задан, теперь он находится на странице MDN для Object.entries () под заголовком Преобразование объекта в карту, в котором говорится:

Преобразование объекта в карту

new Map()Конструктор принимает итератор из entries. С помощью Object.entriesвы можете легко преобразовать из Objectв 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]]
        )
)
Ohar
источник
1
Слияние @ Ohar - х и @ решений TJCrowder в: var buildMap2 = o => new Map(Object.keys(o).map(k => [k, o[k]]));.
7vujy0f0hy
Спасибо, мне тоже нужно было отсортировать ключи объектов!
scottwernervt 05
4

В качестве альтернативы вы можете использовать метод lodash toPairs :

const _ = require('lodash');
const map = new Map(_.toPairs({foo: 'bar'}));
Мауриц Рийк
источник
1

ES6

преобразовать объект в карту:

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

преобразовать карту в объект:

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

Примечание: функция mapToObj предполагает, что ключи карты являются строками (в противном случае не удастся)

Яир Леви
источник
-1

С помощью некоторого JQuery,

const myMap = new Map();
$.each( obj, function( key, value ) {
myMap[key] = value;
});
Патрик Мутуку
источник
4
Я считаю, что в 2019 году мы не должны ИСКАТЬ, что jquery решает проблемы.
illcrx
jQuery все еще присутствует во многих проектах, и это неплохой ответ, просто не оптимальный.
лодкодер