Bagaimana cara mengubah objek biasa menjadi Peta ES6?

128

Untuk beberapa alasan saya tidak dapat menemukan hal sederhana ini di dokumen MDN (mungkin saya melewatkannya).

Saya berharap ini berhasil:

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

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

... tapi baris pertama melempar TypeError: (var)[Symbol.iterator] is not a function

Bagaimana cara membuat Peta dari objek biasa? Apakah saya benar-benar harus mengonversinya terlebih dahulu menjadi array array pasangan kunci-nilai?

callum
sumber
2
FWIW, mungkin ada baiknya mengalihkan jawaban Anda yang diterima dari jawaban saya ke nils ' atau bergi . Object.entriesbenar-benar pendekatan yang lebih baik Object.keys, dan pendekatan fungsi generator bergi sedikit lebih langsung daripada Object.keysatau Object.entries.
TJ Crowder

Jawaban:

195

Ya, Mapkonstruktor mengambil larik pasangan nilai kunci.

Object.entriesadalah metode statis Objek baru yang tersedia di ES2017 (19.1.2.5) .

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

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

Saat ini diterapkan di Firefox 46+ dan Edge 14+ dan versi Chrome yang lebih baru

Jika Anda perlu mendukung lingkungan lama dan transpilasi bukanlah pilihan untuk Anda, gunakan polyfill, seperti yang direkomendasikan oleh georg:

Object.entries = typeof Object.entries === 'function' ? Object.entries : obj => Object.keys(obj).map(k => [k, obj[k]]);
nils
sumber
3
Sebuah "polyfill" akan menjadi agak sepele:Object.entries = obj => Object.keys(obj).map(k => [k, obj[k]])
georg
4
Object.entriesmendarat di Node 7.x tepat (tanpa bendera) btw
Lee Benson
2
Object.entries tidak hanya ada di Node7, tetapi juga merupakan bagian dari spesifikasi akhir ES2017. Jadi, ini harus menjadi jawaban yang diterima, IMO
AnilRedshift
1
@AnilRedshift - Nah, ini atau jawaban fungsi generator , karena OP meminta solusi untuk menghindari perantara.
TJ Crowder
2
Sayangnya, tidak.
Nemanja Milosavljevic
78

Silakan lihat jawaban nils menggunakanObject.entries dan / atau jawaban bergi menggunakan fungsi generator . Meskipun Object.entriesbelum ada dalam spesifikasi ketika pertanyaan diajukan, itu berada di Tahap 4 , jadi aman untuk melakukan polyfill dan digunakan bahkan pada bulan April 2016 (hanya). (Lebih lanjut tentang tahapan di sini .) Dan fungsi generator ada di ES2015. OP secara khusus diminta untuk menghindari perantara, dan sementara generator tidak sepenuhnya menghindarinya, ia melakukan pekerjaan yang lebih baik daripada di bawah atau (sedikit) Object.enties.

FWIW, menggunakan Object.entries:

  • Membuat [name, value]larik untuk diteruskannew Map
  • The Mapkonstruktor memanggil fungsi pada array untuk mendapatkan iterator; array membuat dan mengembalikan objek interator array.
  • The Mappenggunaan konstruktor bahwa objek iterator untuk mendapatkan entri (dalam [name, value]array) dan membangun peta

Menggunakan generator:

  • Membuat objek generator sebagai hasil dari pemanggilan fungsi generator
  • The Mapkonstruktor memanggil fungsi pada objek generator untuk mendapatkan iterator dari itu; objek generator mengembalikan dirinya sendiri
  • The Mapkonstruktor menggunakan objek Generator (sebagai iterator) untuk mendapatkan entri (dalam [name, value]array) dan membangun peta

Jadi: Satu perantara lebih sedikit (array dari Object.entries).

Namun, menggunakan Object.entrieslebih sederhana dan membuat array itu tidak menjadi masalah 99,999% dari waktu. Jadi sungguh, salah satunya. Tapi keduanya lebih baik dari yang di bawah. :-)


Jawaban asli:

Untuk menginisialisasi a Map, Anda dapat menggunakan iterator apa pun yang mengembalikan pasangan kunci / nilai sebagai array, seperti array array:

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

Tidak ada konversi bawaan dari objek ke peta, tetapi itu mudah dilakukan dengan Object.keys:

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

Anda bisa, tentu saja, memberi diri Anda fungsi pekerja untuk mengatasinya:

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

Kemudian

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

Atau inilah versi yang lebih mirip l33t (apakah itu masih ada?):

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

(Ya, Map#setmengembalikan referensi peta. Beberapa berpendapat ini merupakan abusage dari reduce.)

Atau kita benar - benar bisa berlebihan dalam ketidakjelasan:

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

Tidak, saya tidak akan pernah melakukan itu secara nyata. :-)

TJ Crowder
sumber
1
Fusi @ Ohar dan @ solusi TJCrowder ini: var buildMap2 = o => new Map(Object.keys(o).map(k => [k, o[k]]));.
7vujy0f0hy
17
Lihat new Map(Object.entries(object)) stackoverflow.com/a/36644558/798133
Rafael Xavier
jawaban Object.entries harus lebih disukai
Kir
@Kir - Itu atau generatornya , ya. Lihat suntingan saya.
TJ Crowder
31

Apakah saya benar-benar harus mengonversinya terlebih dahulu menjadi array array pasangan kunci-nilai?

Tidak, iterator dari array pasangan nilai-kunci sudah cukup. Anda dapat menggunakan berikut ini untuk menghindari pembuatan array perantara:

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

const map = new Map(entries({foo: 'bar'}));
map.get('foo'); // 'bar'
Bergi
sumber
Contoh yang bagus - hanya sebuah catatan untuk orang lain, Anda mungkin ingin melakukan yang if(!obj.hasOwnProperties(key)) continue;tepat setelah kondisi perulangan for untuk memastikan bahwa Anda tidak menghasilkan properti yang diwarisi dari prototipe objek (kecuali Anda mempercayai objek tersebut, tetapi harus melakukannya kapan saja saat iterasi objek menggunakan insebagai kebiasaan yang baik).
puiu
2
@ Puiu Tidak, seharusnya tidak, dan itu kebiasaan buruk. Jika Anda tidak mempercayai objek tersebut, Anda tidak boleh mempercayai .hasOwnPropertypropertinya juga, dan Anda harus menggunakannyaObject.prototype.hasOwnProperty.call(obj, key)
Bergi
2
Ya, menurut saya tidak mengganggu adalah praktik terbaik. Anda harus mempercayai semua objek yang layak dihitung (yaitu peta-nilai-kunci) untuk tidak memiliki properti warisan yang dapat dihitung. (Dan ya tentu saja hindari menghitung array ). Jika Anda memiliki kasus langka dalam menghitung sesuatu yang lain, dan Anda repot, setidaknya Anda harus melakukannya dengan benar call. Semua konvensi di luar sana yang merekomendasikan obj.hasOwnProperties(key)tampaknya tidak tahu apa yang mereka lakukan.
Bergi
3
Mengubah Objectmenjadi Mapoperasi yang mahal dan OP secara khusus meminta solusi tanpa perantara. Jadi mengapa ini bukan jawaban yang dikecualikan? Nah, menanyakan ini mungkin sia-sia, itu hanya mengganggu saya.
1
@tmvnty Anda mungkin perlu mengeja jenisnya function* entries<T>(obj: T): Generator<readonly [keyof T, T[keyof T]]> {…}. Juga yield [key, obj[key]] as const;akan membantu TypeScript menyadari bahwa itu menghasilkan tupel, bukan array.
Bergi
11

Jawaban oleh Nils menjelaskan bagaimana mengubah objek menjadi peta, yang menurut saya sangat berguna. Namun, OP juga bertanya-tanya di mana informasi ini ada di dokumen MDN. Meskipun mungkin tidak ada di sana ketika pertanyaan awalnya ditanyakan, sekarang ada di halaman MDN untuk Object.entries () di bawah judul Mengonversi Objek ke Peta yang menyatakan:

Mengonversi Objek ke Peta

The new Map()konstruktor menerima sebuah iterable dari entries. Dengan Object.entries, Anda dapat dengan mudah mengonversi dari Objectmenjadi Map:

const obj = { foo: 'bar', baz: 42 }; 
const map = new Map(Object.entries(obj));
console.log(map); // Map { foo: "bar", baz: 42 }
Andrew Willems
sumber
7
const myMap = new Map(
    Object
        .keys(myObj)
        .map(
            key => [key, myObj[key]]
        )
)
Ohar
sumber
1
Fusi @ Ohar dan @ solusi TJCrowder ini: var buildMap2 = o => new Map(Object.keys(o).map(k => [k, o[k]]));.
7vujy0f0hy
Terima kasih, karena saya perlu mengurutkan kunci objek juga!
scottwernervt
4

Alternatifnya Anda bisa menggunakan metode lodash toPairs :

const _ = require('lodash');
const map = new Map(_.toPairs({foo: 'bar'}));
Maurits Rijk
sumber
1

ES6

konversi objek ke peta:

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

konversikan peta menjadi objek:

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

Catatan: fungsi mapToObj mengasumsikan kunci peta adalah string (akan gagal jika tidak)

Yair Levy
sumber
-1

Dengan bantuan beberapa JQuery,

const myMap = new Map();
$.each( obj, function( key, value ) {
myMap[key] = value;
});
Patrick Mutuku
sumber
4
Pada tahun 2019 saya merasa kita tidak harus MENCARI jquery untuk menyelesaikan masalah.
illcrx
jQuery masih dalam banyak proyek dan ini bukan jawaban yang buruk, hanya saja tidak optimal.
boatcoder