Jak przekonwertować zwykły obiekt na mapę ES6?

128

Z jakiegoś powodu nie mogę znaleźć tej prostej rzeczy w dokumentacji MDN (może po prostu jej brakuje).

Spodziewałem się, że to zadziała:

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

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

... ale pierwsza linia rzuca TypeError: (var)[Symbol.iterator] is not a function

Jak zrobić mapę ze zwykłego obiektu? Czy naprawdę muszę najpierw przekonwertować go na tablicę tablic par klucz-wartość?

callum
źródło
2
FWIW, może warto zmienić swoją akceptowaną odpowiedź z mojej na zero lub bergi . Object.entriesnaprawdę jest lepszym podejściem Object.keys, a podejście do funkcji generatora Bergi jest nieco bardziej bezpośrednie niż albo Object.keysalbo Object.entries.
TJ Crowder,

Odpowiedzi:

195

Tak, Mapkonstruktor przyjmuje tablicę par klucz-wartość.

Object.entriesjest nową metodą statyczną Object dostępną w ES2017 (19.1.2.5) .

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

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

Obecnie jest zaimplementowany w przeglądarkach Firefox 46+ i Edge 14+ oraz nowszych wersjach Chrome

Jeśli potrzebujesz obsługi starszych środowisk, a transpilacja nie jest dla Ciebie opcją, użyj polyfill, takiego jak zalecany przez Georg:

Object.entries = typeof Object.entries === 'function' ? Object.entries : obj => Object.keys(obj).map(k => [k, obj[k]]);
zero
źródło
3
„Wypełnienie” będzie raczej trywialne:Object.entries = obj => Object.keys(obj).map(k => [k, obj[k]])
georg
4
Object.entrieswylądował w Node 7.x właściwym (bez flagi) btw
Lee Benson
2
Object.entries nie tylko znajduje się w Node7, ale jest również częścią ostatecznej specyfikacji ES2017. Tak więc powinna to być akceptowana odpowiedź, IMO
AnilRedshift
1
@AnilRedshift - Cóż, ta lub funkcja generatora odpowiada , ponieważ OP poprosił o rozwiązanie unikające pośredników.
TJ Crowder,
2
Niestety tak się nie stanie.
Nemanja Milosavljevic
78

Zobacz odpowiedź nilsa przy użyciuObject.entries i / lub odpowiedź Bergi za pomocą funkcji generatora . Chociaż Object.entriesnie było jeszcze w specyfikacji, gdy zadawano pytanie, było na etapie 4 , więc można go bezpiecznie wypełnić i używać nawet w kwietniu 2016 r. (Właśnie). (Więcej o etapach tutaj .) A funkcje generatora były w ES2015. Program operacyjny wyraźnie poprosił o unikanie pośredników i chociaż generator nie unika tego całkowicie, wykonuje lepszą pracę niż poniżej lub (nieznacznie) Object.enties.

FWIW, używając Object.entries:

  • Tworzy tablicę [name, value]tablic do przekazanianew Map
  • MapKonstruktor wywołuje funkcję na tablicy, aby uzyskać iterator; tablica tworzy i zwraca obiekt pośredniczący tablicy.
  • Do Mapzastosowania konstruktor iterator obiektu, aby uzyskać dane (The [name, value]tablice) i zbudować mapę

Korzystanie z generatora:

  • Tworzy obiekt generatora w wyniku wywołania funkcji generatora
  • MapKonstruktor wywołuje funkcję na tym obiekcie generatora dostać iterator od niego; obiekt generatora zwraca sam siebie
  • MapKonstruktor używa obiektu generatora (jako iterator), aby uzyskać wpisy (te [name, value]tablice) i zbudować mapę

A więc: o jeden pośrednik mniej (tablica z Object.entries).

Jednak używanie Object.entriesjest prostsze, a tworzenie tej tablicy nie stanowi problemu w 99,999% przypadków. Więc naprawdę, albo jeden. Ale oba są lepsze niż poniżej. :-)


Oryginalna odpowiedź:

Aby zainicjować a Map, możesz użyć dowolnego iteratora, który zwraca pary klucz / wartość jako tablice, takie jak tablica tablic:

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

Nie ma wbudowanej konwersji z obiektu na mapę, ale można to łatwo zrobić za pomocą Object.keys:

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

Możesz oczywiście przypisać sobie funkcję pracownika, aby sobie z tym poradzić:

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

Następnie

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

Lub tutaj jest wersja bardziej wyglądająca na l33t (czy to wciąż coś?):

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

(Tak, Map#setzwraca odniesienie mapy. Niektórzy twierdzą, że jest to abusage z reduce).

Lub możemy naprawdę przesadzić w kwestii niejasności:

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

Nie, nigdy bym tego naprawdę nie zrobił. :-)

TJ Crowder
źródło
1
Fuzja @ Ohar i @ rozwiązań TJCrowder za: var buildMap2 = o => new Map(Object.keys(o).map(k => [k, o[k]]));.
7vujy0f0hy
17
Zobacz new Map(Object.entries(object)) stackoverflow.com/a/36644558/798133
Rafael Xavier
preferowane powinny być odpowiedzi Object.entries
Kir
@Kir - To lub generator , tak. Zobacz moją edycję.
TJ Crowder,
31

Czy naprawdę muszę najpierw przekonwertować go na tablicę tablic par klucz-wartość?

Nie, wystarczy iterator tablic par klucz-wartość. Aby uniknąć tworzenia tablicy pośredniej, możesz użyć następujących czynności:

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

const map = new Map(entries({foo: 'bar'}));
map.get('foo'); // 'bar'
Bergi
źródło
Fajny przykład - tylko uwaga dla innych, możesz chcieć zrobić if(!obj.hasOwnProperties(key)) continue;zaraz po warunku pętli for, aby upewnić się, że nie otrzymasz właściwości odziedziczonych z prototypu obiektu (chyba że ufasz obiektowi, ale powinieneś to zrobić mimo wszystko podczas iteracji obiektów przy użyciu injako dobry nawyk).
puiu
2
@Puiu Nie, nie powinieneś i to zły nawyk. Jeśli nie ufasz obiektowi, nie możesz również ufać jego .hasOwnPropertywłasności i musiałbyś z niego korzystaćObject.prototype.hasOwnProperty.call(obj, key)
Bergi
2
Tak, myślę, że nie zawracaj sobie głowy najlepszą praktyką. Powinieneś ufać wszystkim obiektom, które warto wyliczyć (tj. Mapom klucz-wartość), aby nie miały żadnych wyliczalnych dziedziczonych właściwości. (I tak, oczywiście, unikaj wyliczania tablic ). Jeśli masz rzadki przypadek wyliczenia czegoś innego i przeszkadza Ci to przynajmniej powinieneś zrobić to poprawnie call. obj.hasOwnProperties(key)Wydaje się, że wszystkie konwencje, które zalecają , nie mają pojęcia, co robią.
Bergi
3
Przekształcenie A Objectw Mapjest kosztowną operacją, a OP specjalnie poprosił o rozwiązanie bez półproduktów. Dlaczego więc nie jest to wyjątkowa odpowiedź? Cóż, pytanie o to jest prawdopodobnie bezcelowe, po prostu mnie denerwuje.
1
@tmvnty Być może trzeba będzie przeliterować typ function* entries<T>(obj: T): Generator<readonly [keyof T, T[keyof T]]> {…}. yield [key, obj[key]] as const;Pomogłoby również TypeScript uświadomić sobie, że generuje krotki, a nie tablice.
Bergi
11

Odpowiedź Nilsa opisuje, jak konwertować obiekty na mapy, co okazało się bardzo przydatne. Jednak PO zastanawiał się również, gdzie znajdują się te informacje w dokumentach MDN. Chociaż mogło go tam nie być, gdy pierwotnie zadawano pytanie, jest teraz na stronie MDN dla Object.entries () pod nagłówkiem Converting an Object to a Map, który stwierdza:

Konwersja obiektu na mapę

new Map()Konstruktor akceptuje iterowalny z entries. Za pomocą Object.entriesmożesz łatwo przekonwertować z Objectna Map:

const obj = { foo: 'bar', baz: 42 }; 
const map = new Map(Object.entries(obj));
console.log(map); // Map { foo: "bar", baz: 42 }
Andrew Willems
źródło
7
const myMap = new Map(
    Object
        .keys(myObj)
        .map(
            key => [key, myObj[key]]
        )
)
Ohar
źródło
1
Fuzja @ Ohar i @ rozwiązań TJCrowder za: var buildMap2 = o => new Map(Object.keys(o).map(k => [k, o[k]]));.
7vujy0f0hy
Dzięki, ponieważ potrzebowałem też posortować klucze obiektów!
scottwernervt
4

Alternatywnie możesz użyć metody lodash toPairs :

const _ = require('lodash');
const map = new Map(_.toPairs({foo: 'bar'}));
Maurits Rijk
źródło
1

ES6

konwertuj obiekt na mapę:

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

przekonwertuj mapę na obiekt:

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

Uwaga: funkcja mapToObj zakłada, że ​​klucze mapy są ciągami (w przeciwnym razie nie powiedzie się)

Yair Levy
źródło
-1

Z pomocą JQuery,

const myMap = new Map();
$.each( obj, function( key, value ) {
myMap[key] = value;
});
Patrick Mutuku
źródło
4
Czuję, że w 2019 roku nie powinniśmy SZUKAĆ jquery do rozwiązywania problemów.
illcrx
jQuery jest wciąż w wielu projektach i nie jest to zła odpowiedź, po prostu nieoptymalna.
boatcoder