Hingga saat ini, kita telah mempelajari tentang struktur data kompleks berikut:
Objek digunakan untuk menyimpan koleksi yang dikunci.
Array digunakan untuk menyimpan koleksi yang dipesan.
Tapi itu tidak cukup untuk kehidupan nyata. Itu sebabnya Map
dan Set
juga ada.
Map adalah kumpulan item data yang dikunci, sama seperti Object
. Namun perbedaan utamanya adalah Map
mengizinkan kunci jenis apa pun.
Metode dan propertinya adalah:
new Map()
– membuat peta.
map.set(key, value)
– menyimpan nilai dengan kunci.
map.get(key)
– mengembalikan nilai berdasarkan kunci, undefined
jika key
tidak ada di peta.
map.has(key)
– mengembalikan true
jika key
ada, false
jika tidak.
map.delete(key)
– menghapus elemen (pasangan kunci/nilai) dengan kunci.
map.clear()
– menghapus semuanya dari peta.
map.size
– mengembalikan jumlah elemen saat ini.
Misalnya:
biarkan peta = Peta baru(); peta.set('1', 'str1'); // kunci string peta.set(1, 'angka1'); // kunci numerik peta.set(benar, 'bool1'); // kunci boolean // ingat Object biasa? itu akan mengubah kunci menjadi string // Map menyimpan tipenya, jadi keduanya berbeda: peringatan( peta.dapatkan(1) ); // 'nomor1' peringatan( peta.dapatkan('1') ); // 'str1' alert( peta.ukuran ); // 3
Seperti yang bisa kita lihat, tidak seperti objek, kunci tidak diubah menjadi string. Semua jenis kunci dimungkinkan.
map[key]
bukan cara yang tepat untuk menggunakan Map
Meskipun map[key]
juga berfungsi, misalnya kita dapat menyetel map[key] = 2
, ini memperlakukan map
sebagai objek JavaScript biasa, sehingga menyiratkan semua batasan terkait (hanya kunci string/simbol dan seterusnya).
Jadi kita harus menggunakan metode map
: set
, get
dan seterusnya.
Peta juga dapat menggunakan objek sebagai kunci.
Misalnya:
biarkan john = { nama: "John" }; // untuk setiap pengguna, mari kita simpan jumlah kunjungannya biarkan visitsCountMap = Peta baru(); // john adalah kunci peta kunjunganCountMap.set(john, 123); peringatan(visitsCountMap.get(john) ); // 123
Menggunakan objek sebagai kunci adalah salah satu fitur Map
yang paling menonjol dan penting. Hal yang sama tidak berlaku untuk Object
. String sebagai kunci di Object
boleh saja, tapi kita tidak bisa menggunakan Object
lain sebagai kunci di Object
.
Mari kita coba:
biarkan john = { nama: "John" }; misalkan ben = { nama: "Ben" }; biarkan kunjunganCountObj = {}; // mencoba menggunakan suatu objek kunjunganHitungObj[ben] = 234; // coba gunakan objek ben sebagai kuncinya kunjunganHitunganObj[john] = 123; // coba gunakan objek john sebagai kuncinya, objek ben akan diganti // Itu yang tertulis! alert(visitsCountObj["[ObjekObjek]"] ); // 123
Karena visitsCountObj
adalah sebuah objek, ia mengubah semua kunci Object
, seperti john
dan ben
di atas, menjadi string yang sama "[object Object]"
. Pastinya bukan hal yang kita inginkan.
Bagaimana Map
membandingkan kunci
Untuk menguji kesetaraan kunci, Map
menggunakan algoritma SameValueZero. Kira-kira sama dengan persamaan ketat ===
, namun bedanya NaN
dianggap sama dengan NaN
. Jadi NaN
bisa digunakan sebagai kuncinya juga.
Algoritme ini tidak dapat diubah atau disesuaikan.
Rantai
Setiap panggilan map.set
mengembalikan peta itu sendiri, sehingga kita dapat “merantai” panggilan tersebut:
peta.set('1', 'str1') .set(1, 'angka1') .set(benar, 'bool1');
Untuk mengulang map
, ada 3 metode:
map.keys()
– mengembalikan sebuah iterable untuk kunci,
map.values()
– mengembalikan nilai yang dapat diubah,
map.entries()
– mengembalikan iterable untuk entri [key, value]
, ini digunakan secara default di for..of
.
Misalnya:
biarkan resepMap = Peta baru([ ['mentimun', 500], ['tomat', 350], ['bawang', 50] ]); // ulangi kunci (sayuran) for (biarkan sayur dari RecipeMap.keys()) { waspada(sayuran); // mentimun, tomat, bawang bombay } // ulangi nilai (jumlah) for (biarkan jumlah RecipeMap.values()) { peringatan(jumlah); // 500, 350, 50 } // ulangi entri [kunci, nilai]. for (biarkan masuknya RecipeMap) {// sama seperti di RecipeMap.entries() peringatan (entri); // mentimun,500 (dan seterusnya) }
Perintah penyisipan digunakan
Iterasi berjalan dalam urutan yang sama dengan nilai yang dimasukkan. Map
mempertahankan urutan ini, tidak seperti Object
biasa.
Selain itu, Map
memiliki metode forEach
bawaan, mirip dengan Array
:
// menjalankan fungsi untuk setiap pasangan (kunci, nilai). RecipeMap.forEach( (nilai, kunci, peta) => { peringatan(`${kunci}: ${nilai}`); // mentimun: 500 dst });
Saat Map
dibuat, kita bisa meneruskan array (atau iterable lainnya) dengan pasangan kunci/nilai untuk inisialisasi, seperti ini:
// array pasangan [kunci, nilai]. biarkan peta = Peta baru([ ['1', 'str1'], [1, 'nomor1'], [benar, 'bool1'] ]); peringatan( peta.dapatkan('1') ); // str1
Jika kita memiliki objek biasa, dan ingin membuat Map
dari objek tersebut, maka kita dapat menggunakan metode bawaan Object.entries(obj) yang mengembalikan array pasangan kunci/nilai untuk objek persis dalam format tersebut.
Jadi kita bisa membuat peta dari suatu objek seperti ini:
misalkan obj = { nama: "Yohanes", usia: 30 }; biarkan peta = peta baru(Objek.entries(obj)); waspada( peta.get('nama') ); // Yohanes
Di sini, Object.entries
mengembalikan array pasangan kunci/nilai: [ ["name","John"], ["age", 30] ]
. Itulah yang dibutuhkan Map
.
Kita baru saja melihat cara membuat Map
dari objek biasa dengan Object.entries(obj)
.
Ada metode Object.fromEntries
yang melakukan kebalikannya: jika diberi array pasangan [key, value]
, ia akan membuat objek dari pasangan tersebut:
biarkan harga = Object.fromEntries([ ['pisang', 1], ['oranye', 2], ['daging', 4] ]); // sekarang harga = { pisang: 1, jeruk: 2, daging: 4 } alert(harga.oranye); // 2
Kita bisa menggunakan Object.fromEntries
untuk mendapatkan objek biasa dari Map
.
Misalnya kita menyimpan data dalam Map
, tetapi kita harus meneruskannya ke kode pihak ketiga yang mengharapkan objek biasa.
Ini dia:
biarkan peta = Peta baru(); map.set('pisang', 1); map.set('oranye', 2); map.set('daging', 4); biarkan obj = Object.fromEntries(map.entries()); // membuat objek biasa (*) // Selesai! // obj = { pisang: 1, jeruk: 2, daging: 4 } alert(obj.oranye); // 2
Panggilan ke map.entries()
mengembalikan pasangan kunci/nilai yang dapat diubah, persis dalam format yang tepat untuk Object.fromEntries
.
Kita juga bisa memperpendek garis (*)
:
let obj = Object.fromEntries(peta); // hilangkan .entries()
Itu sama, karena Object.fromEntries
mengharapkan objek yang dapat diubah sebagai argumennya. Belum tentu sebuah array. Dan iterasi standar untuk map
mengembalikan pasangan kunci/nilai yang sama dengan map.entries()
. Jadi kita mendapatkan objek biasa dengan kunci/nilai yang sama dengan map
.
Set
adalah kumpulan tipe khusus – “kumpulan nilai” (tanpa kunci), di mana setiap nilai hanya dapat muncul satu kali.
Metode utamanya adalah:
new Set([iterable])
– membuat himpunan, dan jika objek iterable
disediakan (biasanya array), menyalin nilai dari objek tersebut ke dalam himpunan.
set.add(value)
– menambahkan nilai, mengembalikan set itu sendiri.
set.delete(value)
– menghapus nilai, mengembalikan true
jika value
ada pada saat panggilan, jika tidak false
.
set.has(value)
– mengembalikan true
jika nilai ada di set, jika tidak, false
.
set.clear()
– menghapus semuanya dari set.
set.size
– adalah jumlah elemen.
Fitur utamanya adalah panggilan berulang set.add(value)
dengan nilai yang sama tidak menghasilkan apa-apa. Itulah alasan mengapa setiap nilai hanya muncul satu kali dalam satu Set
.
Misalnya, ada pengunjung yang datang, dan kami ingin mengingat semua orang. Namun kunjungan berulang kali tidak boleh menimbulkan duplikat. Seorang pengunjung harus “dihitung” hanya sekali.
Set
adalah hal yang tepat untuk itu:
biarkan set = set baru(); biarkan john = { nama: "John" }; biarkan pete = { nama: "Pete" }; misalkan mary = { nama: "Maria" }; // kunjungan, beberapa pengguna datang berkali-kali set.tambahkan(john); set.tambahkan(pete); set.tambahkan(mary); set.tambahkan(john); set.tambahkan(mary); // set hanya menyimpan nilai unik peringatan( set.ukuran ); // 3 untuk (biarkan pengguna set) { alert(nama pengguna); // John (kemudian Pete dan Mary) }
Alternatif untuk Set
dapat berupa array pengguna, dan kode untuk memeriksa duplikat pada setiap penyisipan menggunakan arr.find. Namun performanya akan jauh lebih buruk, karena metode ini menelusuri seluruh array dan memeriksa setiap elemen. Set
jauh lebih baik dioptimalkan secara internal untuk pemeriksaan keunikan.
Kita dapat mengulang satu set dengan for..of
atau menggunakan forEach
:
let set = new Set(["jeruk", "apel", "pisang"]); for (biarkan nilai set) alert(value); // sama dengan forEach: set.forEach((nilai, nilaiLagi, set) => { peringatan(nilai); });
Perhatikan hal yang lucu. Fungsi panggilan balik yang diteruskan forEach
memiliki 3 argumen: value
, lalu nilai yang sama valueAgain
, dan kemudian objek target. Memang, nilai yang sama muncul dua kali dalam argumen.
Itu untuk kompatibilitas dengan Map
di mana panggilan balik yang diteruskan forEach
memiliki tiga argumen. Pasti terlihat agak aneh. Tapi ini mungkin membantu mengganti Map
dengan Set
dalam kasus tertentu dengan mudah, dan sebaliknya.
Metode yang sama yang dimiliki Map
untuk iterator juga didukung:
set.keys()
– mengembalikan objek yang dapat diubah untuk nilai,
set.values()
– sama seperti set.keys()
, untuk kompatibilitas dengan Map
,
set.entries()
– mengembalikan objek yang dapat diubah untuk entri [value, value]
, ada untuk kompatibilitas dengan Map
.
Map
– adalah kumpulan nilai-nilai kunci.
Metode dan properti:
new Map([iterable])
– membuat peta, dengan pasangan [key,value]
yang dapat iterable
(misalnya array) untuk inisialisasi.
map.set(key, value)
– menyimpan nilai berdasarkan kunci, mengembalikan peta itu sendiri.
map.get(key)
– mengembalikan nilai berdasarkan kunci, undefined
jika key
tidak ada di peta.
map.has(key)
– mengembalikan true
jika key
ada, false
jika tidak.
map.delete(key)
– menghapus elemen dengan kunci, mengembalikan true
jika key
ada pada saat panggilan, jika tidak false
.
map.clear()
– menghapus semuanya dari peta.
map.size
– mengembalikan jumlah elemen saat ini.
Perbedaan dari Object
biasa :
Kunci apa pun, objek bisa menjadi kunci.
Metode tambahan yang mudah, properti size
.
Set
– adalah kumpulan nilai unik.
Metode dan properti:
new Set([iterable])
– membuat set, dengan nilai opsional iterable
(misalnya array) untuk inisialisasi.
set.add(value)
– menambahkan nilai (tidak melakukan apa pun jika ada value
), mengembalikan set itu sendiri.
set.delete(value)
– menghapus nilai, mengembalikan true
jika value
ada pada saat panggilan, jika tidak false
.
set.has(value)
– mengembalikan true
jika nilai ada di set, jika tidak, false
.
set.clear()
– menghapus semuanya dari set.
set.size
– adalah jumlah elemen.
Iterasi pada Map
dan Set
selalu dalam urutan penyisipan, jadi kita tidak bisa mengatakan bahwa koleksi ini tidak berurutan, tapi kita tidak bisa menyusun ulang elemen atau langsung mendapatkan elemen berdasarkan nomornya.
pentingnya: 5
Biarkan arr
menjadi sebuah array.
Buat fungsi unique(arr)
yang akan mengembalikan array dengan item unik arr
.
Misalnya:
fungsi unik(arr) { /* kode Anda */ } biarkan nilai = ["Kelinci", "Krishna", "Kelinci", "Krishna", "Krishna", "Krishna", "Kelinci", "Kelinci", ":-O" ]; peringatan( unik(nilai) ); // Kelinci, Krishna, :-O
PS Di sini string digunakan, tetapi bisa berupa nilai jenis apa pun.
PPS Gunakan Set
untuk menyimpan nilai unik.
Buka kotak pasir dengan tes.
fungsi unik(arr) { return Array.from(Set baru(arr)); }
Buka solusi dengan pengujian di kotak pasir.
pentingnya: 4
Anagram adalah kata-kata yang memiliki jumlah huruf yang sama, tetapi urutannya berbeda.
Misalnya:
tidur siang - panci telinga - adalah - zaman curang - hektar - guru
Tulis fungsi aclean(arr)
yang mengembalikan array yang dibersihkan dari anagram.
Misalnya:
let arr = ["tidur siang", "guru", "penipu", "PAN", "telinga", "era", "hektar"]; waspada( bersih(arr) ); // "tidur siang,guru,telinga" atau "PAN,penipu,era"
Dari setiap kelompok anagram sebaiknya hanya tersisa satu kata, tidak peduli yang mana.
Buka kotak pasir dengan tes.
Untuk menemukan semua anagram, mari bagi setiap kata menjadi huruf dan urutkan. Saat diurutkan berdasarkan huruf, semua anagramnya sama.
Misalnya:
tidur siang, geser -> anp telinga, zaman, adalah -> aer penipu, hektar, guru -> aceehrst ...
Kita akan menggunakan varian yang diurutkan berdasarkan huruf sebagai kunci peta untuk menyimpan hanya satu nilai per setiap kunci:
fungsi bersih(arr) { biarkan peta = Peta baru(); untuk (biarkan kata arr) { // bagi kata demi huruf, urutkan dan gabungkan kembali biarkan diurutkan = Word.toLowerCase().split('').sort().join(''); // (*) map.set(diurutkan, kata); } return Array.dari(peta.nilai()); } let arr = ["tidur siang", "guru", "penipu", "PAN", "telinga", "era", "hektar"]; waspada( bersih(arr) );
Penyortiran huruf dilakukan berdasarkan rangkaian panggilan di baris (*)
.
Untuk kenyamanan, mari kita bagi menjadi beberapa baris:
biarkan diurutkan = kata // PAN .toLowerCase() // menggeser .split('') // ['p','a','n'] .sort() // ['a','n','p'] .bergabung(''); // anp
Dua kata berbeda 'PAN'
dan 'nap'
menerima bentuk pengurutan huruf yang sama 'anp'
.
Baris berikutnya memasukkan kata tersebut ke dalam peta:
map.set(diurutkan, kata);
Jika kita pernah menemukan kata dengan bentuk pengurutan huruf yang sama lagi, maka nilai sebelumnya akan ditimpa dengan kunci yang sama di peta. Jadi kita akan selalu memiliki maksimal satu kata per bentuk huruf.
Pada akhirnya Array.from(map.values())
mengambil alih nilai peta (kita tidak memerlukan kunci dalam hasilnya) dan mengembalikan array dari nilai tersebut.
Di sini kita juga bisa menggunakan objek biasa alih-alih Map
, karena kuncinya adalah string.
Seperti itulah solusinya:
fungsi bersih(arr) { misalkan obj = {}; for (misalkan i = 0; i < arr.length; i++) { biarkan diurutkan = arr[i].toLowerCase().split("").sort().join(""); obj[diurutkan] = arr[i]; } return Objek.nilai(obj); } let arr = ["tidur siang", "guru", "penipu", "PAN", "telinga", "era", "hektar"]; waspada( bersih(arr) );
Buka solusi dengan pengujian di kotak pasir.
pentingnya: 5
Kami ingin mendapatkan array map.keys()
dalam sebuah variabel dan kemudian menerapkan metode khusus array ke dalamnya, misalnya .push
.
Tapi itu tidak berhasil:
biarkan peta = Peta baru(); map.set("nama", "John"); biarkan kunci = peta.kunci(); // Kesalahan: key.push bukan sebuah fungsi kunci.push("lebih");
Mengapa? Bagaimana cara memperbaiki kode agar keys.push
berfungsi?
Itu karena map.keys()
mengembalikan sebuah iterable, tapi bukan sebuah array.
Kita dapat mengubahnya menjadi array menggunakan Array.from
:
biarkan peta = Peta baru(); map.set("nama", "John"); biarkan kunci = Array.dari(peta.kunci()); kunci.push("lebih"); peringatan(kunci); // nama, lebih lanjut