Eşitlik için ECMA6 setlerinin karşılaştırılması


105

İki javascript setini nasıl karşılaştırırsınız? Ben kullanarak çalıştı ==ve ===her iki dönüş false ama.

a = new Set([1,2,3]);
b = new Set([1,3,2]);
a == b; //=> false
a === b; //=> false

Bu iki küme eşdeğerdir, çünkü tanım gereği kümelerin sıralaması yoktur (en azından genellikle değil). Set on MDN belgelerine baktım ve yararlı hiçbir şey bulamadım. Bunun nasıl yapılacağını bilen var mı?


İki küme iki farklı nesnedir. ===değer eşitliği içindir, nesne eşitliği için değildir.
elclanrs

3
her üyenin değerini yineleyin ve karşılaştırın, hepsi aynıysa, ayarlanmış "aynı"
dandavis

1
@dandavis setleri ile üyeler olan değerler.

2
Kümeler ve Haritalar'ın ekleme siparişi olan bir siparişi var - hangi nedenle olursa olsun: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
CodeManX

8
En kötüsü bile new Set([1,2,3]) != new Set([1,2,3]). Bu, Javascript Setini kümeler için kullanışsız hale getirir , çünkü üst küme yinelenen alt kümeler içerecektir. Akla gelen tek geçici çözüm, tüm alt kümeleri dizilere dönüştürmek, her diziyi sıralamak ve ardından her diziyi dize olarak kodlamaktır (örneğin JSON).
7vujy0f0hy

Yanıtlar:


75

Bunu dene:

var a = new Set([1,2,3]);
var b = new Set([1,3,2]);

alert(eqSet(a, b)); // true

function eqSet(as, bs) {
    if (as.size !== bs.size) return false;
    for (var a of as) if (!bs.has(a)) return false;
    return true;
}

Daha işlevsel bir yaklaşım şöyle olacaktır:

var a = new Set([1,2,3]);
var b = new Set([1,3,2]);

alert(eqSet(a, b)); // true

function eqSet(as, bs) {
    return as.size === bs.size && all(isIn(bs), as);
}

function all(pred, as) {
    for (var a of as) if (!pred(a)) return false;
    return true;
}

function isIn(as) {
    return function (a) {
        return as.has(a);
    };
}

allFonksiyon tüm iterable nesneler (örn için çalışır Setve Map).

Array.fromDaha geniş çapta desteklenmiş olsaydı , allişlevi şu şekilde uygulayabilirdik :

function all(pred, as) {
    return Array.from(as).every(pred);
}

Umarım yardımcı olur.


2
Sana adını değiştirmek gerektiğini düşünüyorum hasiçin isPartOfya isInyaelem
Bergi

1
@DavidGiven Evet, JavaScript'teki kümeler ekleme sırasına göre yinelenir: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Aadit M Shah

51
Her gün JavaScript'in şimdiye kadar oluşturulmuş en berbat dil olduğundan daha emin oluyorum. Herkesin sınırlamalarıyla başa çıkabilmek için kendi temel işlevlerini icat etmesi gerekiyor ve bu ES6'da ve biz 2017'deyiz! Set nesnesinin özelliklerine neden bu kadar sık ​​işlev ekleyemediler!
Ghasan

2
@ GhasanAl-Sakkaf, katılıyorum, belki de TC39 bilim adamlarından oluşuyor, ancak pragmatist yok ...
Marecky

1
@TobiasFeil Haskell daha da iyi.
Aadit M Şah

64

Şunları da deneyebilirsiniz:

var a = new Set([1,2,3]);
var b = new Set([1,3,2]);

isSetsEqual = (a, b) => a.size === b.size && [...a].every(value => b.has(value));

console.log(isSetsEqual(a,b)) 


Bir if koşuluna uyduğundan kesinlikle daha iyi bir çözüm
Hugodby

Bu çözümün ne kadar deyimsel olduğunu ve ne kadar okunabilir olduğunu seviyorum! Teşekkürler @Max
daydreamer

31

Lodash , _.isEqual()derin karşılaştırmalar yapan sağlar. Kendi yazınızı yazmak istemiyorsanız bu çok kullanışlıdır. Lodash 4'ten itibaren _.isEqual()Setleri uygun şekilde karşılaştırır.

const _ = require("lodash");

let s1 = new Set([1,2,3]);
let s2 = new Set([1,2,3]);
let s3 = new Set([2,3,4]);

console.log(_.isEqual(s1, s2)); // true
console.log(_.isEqual(s1, s3)); // false

7

Diğer cevap işe yarayacak; işte başka bir alternatif.

// Create function to check if an element is in a specified set.
function isIn(s)          { return elt => s.has(elt); }

// Check if one set contains another (all members of s2 are in s1).
function contains(s1, s2) { return [...s2] . every(isIn(s1)); }

// Set equality: a contains b, and b contains a
function eqSet(a, b)      { return contains(a, b) && contains(b, a); }

// Alternative, check size first
function eqSet(a, b)      { return a.size === b.size && contains(a, b); }

Ancak, bu da bilmenizi değil derin eşitlik karşılaştırma yapmak. Yani

eqSet(Set([{ a: 1 }], Set([{ a: 1 }])

yanlış döndürür. Yukarıdaki iki setin eşit olduğu kabul edilecekse, her öğe üzerinde derin kalite karşılaştırmaları yaparak her iki sette de yinelememiz gerekir. Bir deepEqualrutinin varlığını şart koşuyoruz. O zaman mantık olurdu

// Find a member in "s" deeply equal to some value
function findDeepEqual(s, v) { return [...s] . find(m => deepEqual(v, m)); }

// See if sets s1 and s1 are deeply equal. DESTROYS s2.
function eqSetDeep(s1, s2) {
  return [...s1] . every(a1 => {
    var m1 = findDeepEqual(s2, a1);
    if (m1) { s2.delete(m1); return true; }
  }) && !s2.size;
}

Bu ne yapar: s1'in her bir üyesi için, s2'nin derinden eşit bir üyesi arayın. Bulunursa, tekrar kullanılmaması için silin. S1'deki tüm elemanlar s2'de bulunursa ve s2 tükenirse iki küme derinden eşittir . Test edilmemiş.

Bunu yararlı bulabilirsiniz: http://www.2ality.com/2015/01/es6-set-operations.html .


6

Bu çözümlerin hiçbiri, kümeler kümesi gibi bir veri yapısına beklenen işlevselliği "geri" getirmez . Şu anki durumunda, Javascript Kümesi bu amaç için kullanışsızdır çünkü üst küme, Javascript'in yanlış bir şekilde farklı olarak gördüğü yinelenen alt kümeler içerecektir. I düşün tek çözüm, her bir alt-kümesini dönüştürmektedir Array bunu, sıralama ve daha sonra da kodlayan dize (örneğin JSON).

Çözüm

var toJsonSet = aset /* array or set */ => JSON.stringify([...new Set(aset)].sort()); 
var fromJsonSet = jset => new Set(JSON.parse(jset));

Temel kullanım

var toJsonSet = aset /* array or set */ => JSON.stringify([...new Set(aset)].sort()); 
var fromJsonSet = jset => new Set(JSON.parse(jset));

var [s1,s2] = [new Set([1,2,3]), new Set([3,2,1])];
var [js1,js2] = [toJsonSet([1,2,3]), toJsonSet([3,2,1])]; // even better

var r = document.querySelectorAll("td:nth-child(2)");
r[0].innerHTML = (toJsonSet(s1) === toJsonSet(s2)); // true
r[1].innerHTML = (toJsonSet(s1) == toJsonSet(s2)); // true, too
r[2].innerHTML = (js1 === js2); // true
r[3].innerHTML = (js1 == js2); // true, too

// Make it normal Set:
console.log(fromJsonSet(js1), fromJsonSet(js2)); // type is Set
<style>td:nth-child(2) {color: red;}</style>

<table>
<tr><td>toJsonSet(s1) === toJsonSet(s2)</td><td>...</td></tr>
<tr><td>toJsonSet(s1) == toJsonSet(s2)</td><td>...</td></tr>
<tr><td>js1 === js2</td><td>...</td></tr>
<tr><td>js1 == js2</td><td>...</td></tr>
</table>

Nihai test: set seti

var toSet = arr => new Set(arr);
var toJsonSet = aset /* array or set */ => JSON.stringify([...new Set(aset)].sort()); 
var toJsonSet_WRONG = set => JSON.stringify([...set]); // no sorting!

var output = document.getElementsByTagName("code"); 
var superarray = [[1,2,3],[1,2,3],[3,2,1],[3,6,2],[4,5,6]];
var superset;

Experiment1:
    superset = toSet(superarray.map(toSet));
    output[0].innerHTML = superset.size; // incorrect: 5 unique subsets
Experiment2:
    superset = toSet([...superset].map(toJsonSet_WRONG));
    output[1].innerHTML = superset.size; // incorrect: 4 unique subsets
Experiment3:
    superset = toSet([...superset].map(toJsonSet));
    output[2].innerHTML = superset.size; // 3 unique subsets
Experiment4:
    superset = toSet(superarray.map(toJsonSet));
    output[3].innerHTML = superset.size; // 3 unique subsets
code {border: 1px solid #88f; background-color: #ddf; padding: 0 0.5em;}
<h3>Experiment 1</h3><p>Superset contains 3 unique subsets but Javascript sees <code>...</code>.<br>Let’s fix this... I’ll encode each subset as a string.</p>
<h3>Experiment 2</h3><p>Now Javascript sees <code>...</code> unique subsets.<br>Better! But still not perfect.<br>That’s because we didn’t sort each subset.<br>Let’s sort it out...</p>
<h3>Experiment 3</h3><p>Now Javascript sees <code>...</code> unique subsets. At long last!<br>Let’s try everything again from the beginning.</p>
<h3>Experiment 4</h3><p>Superset contains 3 unique subsets and Javascript sees <code>...</code>.<br><b>Bravo!</b></p>


1
Harika çözüm! Ve eğer bir dizi [...set1].sort().toString() === [...set2].sort().toString()

3

Yaklaşımınızın yanlış döndürmesinin nedeni, iki farklı nesneyi karşılaştırmanızdır (aynı içeriğe sahip olsalar bile), bu nedenle iki farklı nesneyi (referansları değil, nesneleri) karşılaştırmanız her zaman yanlışlık verir.

Aşağıdaki yaklaşım, iki grubu bir araya getirir ve aptalca boyutu karşılaştırır. Aynıysa, aynıdır:

const a1 = [1,2,3];
const a2 = [1,3,2];
const set1 = new Set(a1);
const set2 = new Set(a2);

const compareSet = new Set([...a1, ...a2]);
const isSetEqual = compareSet.size === set2.size && compareSet.size === set1.size;
console.log(isSetEqual);

Upside : Çok basit ve kısa. Harici kitaplık yok, yalnızca vanilya JS

Dezavantajı : Muhtemelen değerleri tekrarlamaktan daha yavaş olacak ve daha fazla alana ihtiyacınız olacak.


1

İki nesnenin ==, === ile karşılaştırılması

İki nesneyi karşılaştırmak için ==veya ===operatörü kullanırken , false bu nesneler aynı nesneye başvurmadıkça her zaman elde edersiniz . Örneğin:

var a = b = new Set([1,2,3]); // NOTE: b will become a global variable
a == b; // <-- true: a and b share the same object reference

Aksi takdirde, ==, nesne aynı değerleri içerse bile yanlışa eşittir:

var a = new Set([1,2,3]);
var b = new Set([1,2,3]);
a == b; // <-- false: a and b are not referencing the same object

Manuel karşılaştırmayı düşünmeniz gerekebilir

ECMAScript 6'da, setleri önceden dizilere dönüştürebilirsiniz, böylece aralarındaki farkı görebilirsiniz:

function setsEqual(a,b){
    if (a.size !== b.size)
        return false;
    let aa = Array.from(a); 
    let bb = Array.from(b);
    return aa.filter(function(i){return bb.indexOf(i)<0}).length==0;
}

NOT: Array.from standart ECMAScript 6 özelliklerinden biridir ancak modern tarayıcılarda yaygın olarak desteklenmemektedir. Uyumluluk tablosunu buradan kontrol edin: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from#Browser_compatibility


1
Bu b, içinde olmayan üyeyi belirleyemez ami?

1
@torazaburo Gerçekten. En iyi yolu üyeleri olmadığını kontrol atlamak için bdeğildir aolmadığını kontrol etmektir a.size === b.size.
Aadit M Shah

1
a.size === b.sizeİlk önce gerekli değilse, bireysel elemanların karşılaştırmasını kısa devreye koyun ?

2
Boyut farklıysa, tanım gereği kümeler eşit değildir, bu nedenle önce bu koşulu kontrol etmek daha iyidir.

1
Buradaki diğer sorun, kümelerin doğası gereği , diziler üzerindeki işlemin hasaksine, kümeler üzerindeki işlemin çok verimli olacak şekilde tasarlanmasıdır indexOf. Bu nedenle, filtre işlevinizi olarak değiştirmek mantıklı olacaktır return !b.has(i). Bu aynı zamanda bbir diziye dönüştürme ihtiyacını da ortadan kaldırır .

1

Set.prototype.isEqual () için hızlı bir çoklu dolgu oluşturdum

Set.prototype.isEqual = function(otherSet) {
    if(this.size !== otherSet.size) return false;
    for(let item of this) if(!otherSet.has(item)) return false;
    return true;
}

Github Gist - Set.prototype.isEqual


1

Kabul edilen cevaba dayanarak, desteğini varsayarsak Array.from, işte tek satırlık bir çözüm:

function eqSet(a, b) {
    return a.size === b.size && Array.from(a).every(b.has.bind(b));
}

Veya ok işlevlerini ve spread operatörünü varsayan gerçek bir tek eqSet = (a,b) => a.size === b.size && [...a].every(b.has.bind(b))
John Hoffer

1

Kümeler yalnızca ilkel veri türlerini içeriyorsa veya kümelerin içindeki nesneler referans eşitliğine sahipse, daha basit bir yol vardır.

const isEqualSets = (set1, set2) => (set1.size === set2.size) && (set1.size === new Set([...set1, ...set2]).size);


0

Bu yaklaşımı testlerde takip ediyorum:

let setA = new Set(arrayA);
let setB = new Set(arrayB);
let diff = new Set([...setA].filter(x => !setB.has(x)));
expect([...diff].length).toBe(0);

5
Bir saniye bekleyin ... bu sadece A'nın B'nin sahip olmadığı unsurlara sahip olup olmadığını kontrol eder? B'nin, A'nın sahip olmadığı unsurlara sahip olup olmadığını kontrol etmez. Eğer denersen a=[1,2,3]ve b=[1,2,3,4]aynı olduklarını söyler. Öyleyse sanırım ekstra bir çeke ihtiyacınız varsetA.size === setB.size

0

@Aadit M Shah'ın cevabına göre çok küçük bir değişiklik:

/**
 * check if two sets are equal in the sense that
 * they have a matching set of values.
 *
 * @param {Set} a 
 * @param {Set} b
 * @returns {Boolean} 
 */
const areSetsEqual = (a, b) => (
        (a.size === b.size) ? 
        [...a].every( value => b.has(value) ) : false
);

Son babelin bazı tuhaflıkları nedeniyle benim yaptığım gibi başka biri sorun yaşıyorsa, buraya açık bir koşul eklemek zorunda kaldı.

(Ayrıca çoğul için areyüksek sesle okumanın biraz daha sezgisel olduğunu düşünüyorum 🙃)


-1

1) Boyutların eşit olup olmadığını kontrol edin. Değilse, eşit değillerdir.

2) A'nın her bir öğesini yineleyin ve B'de var olanı kontrol edin. Biri başarısız olursa geri dönün unequal

3) Yukarıdaki 2 koşul başarısız olursa, bu eşit oldukları anlamına gelir.

let isEql = (setA, setB) => {
  if (setA.size !== setB.size)
    return false;
  
  setA.forEach((val) => {
    if (!setB.has(val))
      return false;
  });
  return true;
}

let setA = new Set([1, 2, {
  3: 4
}]);
let setB = new Set([2, {
    3: 4
  },
  1
]);

console.log(isEql(setA, setB));

2) Yöntem 2

let isEql = (A, B) => {
  return JSON.stringify([...A].sort()) == JSON.stringify([...B].sort());
}

let res = isEql(new Set([1, 2, {3:4}]), new Set([{3:4},1, 2]));
console.log(res);


Bu cevap tamamen yanlış. forEachYöntemdeki dönüş ifadesi , ana işlevi geri döndürmez.
xaviert
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.