Як перетворити звичайний об’єкт у карту 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- це новий статичний метод об'єкта, доступний у 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]])
геор

4
Object.entriesприземлився у вузлі 7.x належним чином (без прапора) btw
Лі Бенсон

2
Object.entries не тільки в Node7, він також є частиною остаточної специфікації ES2017. Таким чином, це має бути прийнята відповідь, IMO
AnilRedshift

1
@AnilRedshift - Ну, це відповідь або функція генератора , оскільки ОП попросило рішення уникати посередників.
TJ Crowder

2
На жаль, не буде.
Nemanja

78

Будь ласка, дивіться відповіді нулів, використовуючиObject.entries та / або відповідь Бергі, використовуючи функцію генератора . Хоча Object.entriesще не було в специфіці, коли питання було задано, це було на 4 етапі , настільки безпечно для поліфазування та використання навіть у квітні 2016 року (просто). (Детальніше про етапи тут .) А функції генератора були в ES2015. ОП спеціально просило уникати посередників, і хоча генератор цього зовсім не уникає, він робить кращу роботу, ніж нижче або (трохи) 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повертає посилання на карту. Деякі стверджують , це abusage з reduce.)

Або ми дійсно можемо перейти за межі незрозумілості:

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

Ні, я б ніколи цього не робив по-справжньому. :-)


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
Кір,

@Kir - Це або генератор , так. Дивіться мою редакцію.
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

2
@Puiu Ні, не слід, і це шкідлива звичка. Якщо ви не довіряєте об'єкту, ви також не повинні довіряти його .hasOwnPropertyмайну, і вам доведеться скористатисяObject.prototype.hasOwnProperty.call(obj, key)
Бергі

2
Так, я думаю, що не турбуватися - це найкраща практика. Ви повинні довіряти всім об'єктам, які варто перерахувати (тобто карта-значення-карти), щоб вони не мали жодних перелічених властивостей. (І так, звичайно, уникайте перерахування масивів ). Якщо у вас є рідкісний випадок перерахування чогось іншого, і ви турбуєтесь, вам слід принаймні зробити це належним чином call. Усі конвенції, які рекомендують, obj.hasOwnProperties(key)схоже, не мають поняття, що вони роблять.
Бергі

3
Перетворення Objectна «а» Map- це дорога операція, і ОП спеціально вимагає рішення без проміжних продуктів. Так чому це не є виключеною відповіддю? Що ж, просити це, мабуть, марно, це мене просто дратує.

1
@tmvnty Можливо, вам потрібно буде прописати тип function* entries<T>(obj: T): Generator<readonly [keyof T, T[keyof T]]> {…}. Також yield [key, obj[key]] as const;допоможе TypeScript усвідомити, що він дає кортежі, а не масиви.
Бергі

11

Відповідь Нілса описує, як перетворити об’єкти на карти, що мені здалося дуже корисним. Однак, ОП також цікавилося, де ця інформація знаходиться в документах 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]]
        )
)

1
Злиття рішень @ Ohar та @ TJCrowder : var buildMap2 = o => new Map(Object.keys(o).map(k => [k, o[k]]));.
7vujy0f0hy

Дякую, як мені теж потрібно було сортувати клавіші об’єкта!
scottwernervt


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 році я відчуваю, що нам не варто ДУЖЕ ЖИТТЯ, щоб вирішити проблеми.
illcrx

jQuery все ще є у багатьох проектах, і це не погана відповідь, просто не оптимальна.
човенкодер
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.