Bagaimana cara memetakan / mengurangi / memfilter Set di JavaScript?

131

Apakah ada cara untuk map/ reduce/ filter/ etc a Setdi JavaScript atau saya harus menulis sendiri?

Berikut ini beberapa Set.prototypeekstensi yang masuk akal

Set.prototype.map = function map(f) {
  var newSet = new Set();
  for (var v of this.values()) newSet.add(f(v));
  return newSet;
};

Set.prototype.reduce = function(f,initial) {
  var result = initial;
  for (var v of this) result = f(result, v);
  return result;
};

Set.prototype.filter = function filter(f) {
  var newSet = new Set();
  for (var v of this) if(f(v)) newSet.add(v);
  return newSet;
};

Set.prototype.every = function every(f) {
  for (var v of this) if (!f(v)) return false;
  return true;
};

Set.prototype.some = function some(f) {
  for (var v of this) if (f(v)) return true;
  return false;
};

Mari kita ambil set kecil

let s = new Set([1,2,3,4]);

Dan beberapa fungsi kecil yang bodoh

const times10 = x => x * 10;
const add = (x,y) => x + y;
const even = x => x % 2 === 0;

Dan lihat bagaimana mereka bekerja

s.map(times10);    //=> Set {10,20,30,40}
s.reduce(add, 0);  //=> 10
s.filter(even);    //=> Set {2,4}
s.every(even);     //=> false
s.some(even);      //=> true

Bukankah itu bagus? Ya, saya juga berpikir begitu. Bandingkan dengan penggunaan iterator yang jelek

// puke
let newSet = new Set();
for (let v in s) {
  newSet.add(times10(v));
}

Dan

// barf
let sum = 0;
for (let v in s) {
  sum = sum + v;
}

Apakah ada cara yang lebih baik untuk menyelesaikan mapdan reducemenggunakan SetJavaScript?

Terima kasih
sumber
Masalah dengan pengurangan peta Setadalah Set tidak Functors.
Bartek Banachewicz
@ BartekBanachewicz ya itu semacam masalah ... kan?
Terima kasih
2
Nah, pertimbangkan var s = new Set([1,2,3,4]); s.map((a) => 42);. Ini mengubah jumlah elemen, yang mapbiasanya tidak seharusnya dilakukan. Lebih buruk lagi jika Anda hanya membandingkan bagian-bagian dari objek yang disimpan, karena secara teknis itu tidak ditentukan mana yang akan Anda dapatkan.
Bartek Banachewicz
Saya telah mempertimbangkan itu, tetapi saya tidak yakin saya (secara pribadi) akan menganggap itu tidak valid. OK jadi setidaknya forEachada untuk skenario itu, tapi mengapa tidak reduce?
Terima kasih
4
Beberapa bacaan terkait: esdiscuss.org/topic/set-some-every-reduce-filter-map-methods
CodingIntrigue

Jawaban:

105

Cara singkat untuk melakukannya adalah mengonversinya menjadi array melalui operator spread ES6.

Maka semua fungsi array tersedia untuk Anda.

const mySet = new Set([1,2,3,4]);
[...mySet].reduce()
ZephDavies
sumber
1
Karena fungsi tidak tersedia untuk Set! Ini adalah solusi yang lengkap, dibimbing dan dipahami yang belum hadir dalam topik ini. Fakta 'butuh waktu lebih lama' adalah harga yang menyedihkan untuk membayar solusi sampai Set mengimplementasikan fitur-fitur ini!
ZephDavies
1
Apa perbedaan antara ini dan Array.from
pete
9
Bagi saya setidaknya, perbedaan antara ini dan Array.fromitu Array.fromberfungsi dengan TypeScript. Menggunakan [...mySet]memberikan kesalahan:TS2461: Type 'Set<number>' is not an array type.
Mikal Madsen
1
Untuk spread vs Array.from (), lihat stackoverflow.com/a/40549565/5516454 Pada dasarnya, keduanya dapat digunakan di sini. Array.from () juga dapat melakukan objek mirip array yang tidak mengimplementasikan @@iteratormetode.
ZephDavies
masih tidak bekerja untuk saya dengan naskah. Saya mendapatkanERROR TypeError: this.sausages.slice is not a function
Simon_Weaver
22

Untuk meringkas diskusi dari komentar: sementara tidak ada alasan teknis untuk tidak memilikinyareduce , saat ini tidak disediakan dan kami hanya bisa berharap itu berubah dalam ES7.

Adapun map, memanggilnya saja bisa melanggarSet kendala, sehingga kehadirannya di sini mungkin bisa diperdebatkan.

Pertimbangkan pemetaan dengan fungsi (a) => 42- ini akan mengubah ukuran set ke 1, dan ini mungkin atau mungkin bukan yang Anda inginkan.

Jika Anda melanggar dengan itu karena misalnya Anda akan melipat, Anda dapat menerapkan mapbagian pada setiap elemen sebelum melewati mereka reduce, sehingga menerima bahwa koleksi perantara ( yang bukan Set pada saat ini ) itu akan dikurangi mungkin memiliki elemen yang digandakan. Ini pada dasarnya setara dengan mengkonversi ke Array untuk melakukan pemrosesan.

Bartek Banachewicz
sumber
1
Ini sebagian besar baik, kecuali (menggunakan kode di atas), s.map(a => 42)akan menghasilkan Set { 42 }sehingga hasil yang dipetakan akan memiliki panjang yang berbeda tetapi tidak akan ada elemen "duplikat". Mungkin perbarui kata-katanya dan saya akan menerima jawaban ini.
Terima kasih
@naomik Oh derp Saya baru saja menyelesaikan kopi pertama saya ketika menulis itu. Pada tampilan kedua, koleksi perantara yang disahkan untuk mengurangi mungkin memiliki elemen langsung jika Anda menerimanya bukan set - maksud saya.
Bartek Banachewicz
Oh saya mengerti - peta harus dipetakan ke jenis yang sama, maka kemungkinan tabrakan di set tujuan. Ketika saya menemukan pertanyaan ini, saya berpikir peta akan memetakan ke array dari set. (seolah-olah Anda melakukan set.toArray (). map () `
Simon_Weaver
2
Di Scala dan Haskell, set mendukung operasi peta - ini dapat mengurangi jumlah elemen dalam set.
Velizar Hristov
8

Penyebab kurangnya map/ reduce/ filterdi Map/ Setkoleksi tampaknya kekhawatiran terutama konseptual. Seharusnya masing-masing jenis koleksi dalam Javascript benar-benar menentukan metode iteratif sendiri hanya untuk memungkinkan ini

const mySet = new Set([1,2,3]);
const myMap = new Map([[1,1],[2,2],[3,3]]);

mySet.map(x => x + 1);
myMap.map(([k, x]) => [k, x + 1]);

dari pada

new Set(Array.from(mySet.values(), x => x + 1));
new Map(Array.from(myMap.entries(), ([k, x]) => [k, x + 1]));

Alternatifnya adalah menentukan peta / perkecil / filter sebagai bagian dari protokol iterable / iterator, karena entries/ values/ keyskembaliIterator s. Bisa dibayangkan bahwa tidak semua iterable juga "bisa dipetakan". Alternatif lain adalah menentukan "protokol pengumpulan" yang terpisah untuk tujuan ini.

Namun, saya tidak tahu diskusi saat ini tentang topik ini di ES.


sumber