JavaScript'te bir Küme nasıl eşlenir / küçültülür / filtrelenir?


132

JavaScript'te map/ reduce/ filter/ etc a için herhangi bir yol var mı yoksa Setkendim yazmam gerekecek mi?

İşte bazı mantıklı Set.prototypeuzantılar

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;
};

Hadi küçük bir set alalım

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

Ve bazı aptal küçük işlevler

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

Ve nasıl çalıştıklarını gör

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

Güzel değil mi? Evet, ben de öyle düşünüyorum. Bunu çirkin yineleyici kullanımıyla karşılaştırın

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

Ve

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

JavaScript'te a'yı gerçekleştirmenin mapve reducekullanmanın daha iyi bir yolu var mı Set?


Harita küçültme ile ilgili sorun Set, Kümelerin Functor olmamasıdır.
Bartek Banachewicz

@BartekBanachewicz evet bu bir tür sorun ... değil mi?
teşekkür ederim

2
Bir düşünün var s = new Set([1,2,3,4]); s.map((a) => 42);. mapGenellikle yapılması gerekmeyen öğelerin sayısını değiştirir . Daha da kötüsü, yalnızca tutulan nesnelerin parçalarını karşılaştırıyorsanız, çünkü o zaman teknik olarak hangisini alacağınız belirsizdir.
Bartek Banachewicz

Bunu düşünmüştüm ama (şahsen) bunu geçersiz sayacağımdan emin değilim. Tamam, en azından forEachbu senaryo için var, ama reduceo zaman neden hayır ?
teşekkür ederim

Yanıtlar:


105

Bunu yapmanın kısa bir yolu, onu ES6 yayılma operatörü aracılığıyla bir diziye dönüştürmektir.

O zaman tüm dizi işlevleri sizin için kullanılabilir.

const mySet = new Set([1,2,3,4]);
[...mySet].reduce()

1
Çünkü fonksiyonlar Set için mevcut değil! Bu, bu konuda henüz mevcut olmayan eksiksiz, kılavuzlu ve anlaşılmış bir çözümdür. Set bu özellikleri uygulayana kadar 'daha uzun sürmesi' bir geçici çözüm için ödenmesi üzücü bir bedel!
ZephDavies

1
Bu ve Array arasındaki fark nedir?
pete

9
En azından benim için Array.from, bununla arasındaki fark Array.fromTypeScript ile çalışıyor olmasıdır. Kullanmak [...mySet]şu hatayı veriyor:TS2461: Type 'Set<number>' is not an array type.
Mikal Madsen

1
Array.from () ile spread karşılaştırması için stackoverflow.com/a/40549565/5516454 adresine bakın Temel olarak, her ikisi de burada kullanılabilir. Array.from () ayrıca @@iteratoryöntemi uygulamayan dizi benzeri nesneler de yapabilir .
ZephDavies

hala typcript ile benim için çalışmıyor. AnlıyorumERROR TypeError: this.sausages.slice is not a function
Simon_Weaver

22

Tartışmayı yorumlardan özetlemek gerekirse: setin sahip olmaması için teknik nedenler bulunmamakla birlikte reduce, şu anda sağlanmamaktadır ve yalnızca ES7'de değişmesini umabiliriz.

Gelince map, onu tek başına çağırmak Setkısıtlamayı ihlal edebilir , bu nedenle buradaki varlığı tartışılabilir olabilir.

Bir işlevle eşlemeyi düşünün (a) => 42- setin boyutunu 1 olarak değiştirir ve bu istediğiniz olabilir veya olmayabilir .

Bunu ihlal etmekte sorun yoksa, örneğin yine de katlayacağınız için, mapparçayı her öğeye geçirmeden hemen önce uygulayabilirsiniz reduce, böylece ara koleksiyonun ( bu noktada bir Set değildir ) azaltılacak yinelenen öğelere sahip olabilir. Bu, işlem yapmak için Array'e dönüştürmeye esasen eşdeğerdir.


1
Bu, (yukarıdaki kodu kullanarak) dışında, çoğunlukla iyi s.map(a => 42)neden olur Set { 42 }elemanları "çoğaltılmış" eşleştirilmiş sonucu farklı uzunlukta olacak şekilde ama olmayacaktır. Belki ifadeyi güncellerseniz bu cevabı kabul ederim.
Teşekkürler

@naomik Oh derp Bunu yazarken ilk kahvemi yeni bitiriyordum. İkinci bakışta, azaltmaya geçilen ara koleksiyon, bunun bir set olmadığını kabul ederseniz, hemen unsurlara sahip olabilir - bunu kastediyorum.
Bartek Banachewicz

Oh anladım - haritanın aynı türle eşlenmesi gerekiyor, bu nedenle hedef kümesinde olası çarpışmalar. Bu soruyu bulduğumda, haritanın bir kümeden bir diziye eşleneceğini düşünüyordum. (set.toArray (). map () ``
Simon_Weaver

2
Scala ve Haskell'de setler bir harita işlemini destekler - setteki elemanların sayısını azaltabilir.
Velizar Hristov

8

map/ reduce/ filterOn Map/ Setkoleksiyon eksikliğinin nedeni temelde kavramsal kaygılar gibi görünmektedir. Javascript'teki her koleksiyon türü aslında yalnızca buna izin vermek için kendi yinelemeli yöntemlerini belirtmeli mi?

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]);

onun yerine

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

Bir alternatif, entries/ values/ keysreturn Iterators beri yinelenebilir / yineleyici protokolünün bir parçası olarak eşleme / azaltma / filtre belirtmekti . Her yinelenebilirliğin aynı zamanda "eşlenebilir" olmadığı da düşünülebilir. Diğer bir alternatif, tam da bu amaç için ayrı bir "toplama protokolü" belirlemekti.

Ancak, ES'de bu konuyla ilgili güncel tartışmayı bilmiyorum.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.