so sánh các bộ ECMA6 để có sự bình đẳng


102

Làm thế nào để bạn so sánh hai bộ javascript? Tôi đã thử sử dụng =====nhưng cả hai đều trả về sai.

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

Hai tập hợp này là tương đương, bởi vì theo định nghĩa, các tập hợp không có thứ tự (ít nhất là không thường xuyên). Tôi đã xem tài liệu về Đặt trên MDN và không tìm thấy gì hữu ích. Bất cứ ai biết làm thế nào để làm điều này?


Hai tập hợp là hai đối tượng khác nhau. ===là bình đẳng giá trị, không bình đẳng đối tượng.
elclanrs

3
lặp và so sánh giá trị của mỗi thành viên, nếu tất cả cùng, thiết lập là "tương tự"
dandavis

1
@dandavis Với tập hợp, các thành viên các giá trị.

2
Bộ và Maps không có một trật tự, mà là thứ tự chèn - vì lý do gì: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/...
CodeManX

8
Tồi tệ nhất, ngay cả new Set([1,2,3]) != new Set([1,2,3]). Điều này làm cho Javascript Set trở nên vô dụng đối với các tập hợp vì tập siêu sẽ chứa các tập con trùng lặp. Cách giải quyết duy nhất nảy sinh trong tâm trí là chuyển đổi tất cả các tập con thành mảng, sắp xếp từng mảng và sau đó mã hóa từng mảng dưới dạng chuỗi (ví dụ: JSON).
7vujy0f0hy

Câu trả lời:


73

Thử cái này:

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

Một cách tiếp cận chức năng hơn sẽ là:

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

Các allchức năng làm việc cho tất cả các đối tượng iterable (ví dụ SetMap).

Nếu Array.fromđược hỗ trợ rộng rãi hơn thì chúng tôi có thể đã triển khai allchức năng như:

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

Hy vọng rằng sẽ giúp.


2
Tôi nghĩ bạn nên thay đổi tên của hasđể isPartOfhoặc isInhoặcelem
Bergi

@Bergi Tôi đã đổi tên của hàm hasthành isIn.
Aadit M Shah,

1
@DavidGiven Có, các bộ trong JavaScript được lặp lại theo thứ tự chèn: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Aadit M Shah

48
Mỗi ngày, tôi yên tâm hơn về việc JavaScript là ngôn ngữ tệ hại nhất từng được tạo ra. Mọi người đều phải phát minh ra các chức năng cơ bản của riêng mình để đối phó với hạn chế của nó, và điều này là trong ES6, và chúng tôi là năm 2017! Tại sao họ không thể thêm các hàm thường xuyên như vậy vào đặc tả của đối tượng Set!
Ghasan

2
@ GhasanAl-Sakkaf, tôi đồng ý, đó là lẽ đó TC39 bao gồm các nhà khoa học, nhưng không thực dụng ...
Marecky

59

Bạn cũng có thể thử:

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


Chắc chắn là một giải pháp tốt hơn vì nó phù hợp với bên trong một điều kiện if
Hugodby

Tôi thích giải pháp này thành ngữ như thế nào và có thể đọc được bao nhiêu! Cảm ơn @Max
daydreamer

29

lodash cung cấp _.isEqual(), so sánh sâu. Điều này rất tiện dụng nếu bạn không muốn viết thư của riêng mình. Kể từ lodash 4, hãy _.isEqual()so sánh đúng các Bộ.

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

Câu trả lời khác sẽ hoạt động tốt; đây là một thay thế khác.

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

Tuy nhiên, hãy lưu ý rằng điều này không tạo ra sự so sánh bình đẳng sâu sắc. Vì thế

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

sẽ trả về false. Nếu hai tập hợp trên được coi là bằng nhau, chúng ta cần lặp lại cả hai tập hợp để thực hiện so sánh chất lượng sâu trên từng phần tử. Chúng tôi quy định sự tồn tại của một deepEqualthói quen. Sau đó, logic sẽ là

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

Điều này có tác dụng gì: đối với mỗi thành viên của s1, hãy tìm kiếm một thành viên bình đẳng sâu sắc của s2. Nếu tìm thấy, hãy xóa nó để không thể sử dụng lại. Hai tập hợp là bằng nhau sâu sắc nếu tất cả các phần tử trong s1 được tìm thấy trong s2 s2 hết. Chưa được kiểm chứng.

Bạn có thể thấy điều này hữu ích: http://www.2ality.com/2015/01/es6-set-operations.html .


6

Không có giải pháp nào trong số này mang lại “trở lại” chức năng mong đợi cho cấu trúc dữ liệu chẳng hạn như tập hợp các bộ. Trong trạng thái hiện tại của nó, Javascript Set là vô ích cho mục đích này vì superset sẽ chứa các tập con trùng lặp, mà Javascript sai coi là khác biệt. Giải pháp duy nhất tôi có thể nghĩ đến là chuyển đổi từng tập hợp con thành Mảng , sắp xếp nó và sau đó mã hóa thành Chuỗi (ví dụ: JSON).

Giải pháp

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

Cách sử dụng cơ bản

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>

Kiểm tra cuối cùng: tập hợp các bộ

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
Giải pháp tuyệt vời! Và nếu bạn biết rằng bạn vừa nhận được một tập hợp các chuỗi hoặc số, nó chỉ trở nên[...set1].sort().toString() === [...set2].sort().toString()

3

Lý do tại sao cách tiếp cận của bạn trả về false là bởi vì bạn đang so sánh hai đối tượng khác nhau (ngay cả khi chúng có cùng nội dung), do đó so sánh hai đối tượng khác nhau (không phải tham chiếu mà là đối tượng) luôn trả về sai.

Cách tiếp cận sau đây hợp nhất hai tập hợp thành một và chỉ so sánh kích thước một cách ngu ngốc. Nếu nó giống nhau, nó giống nhau:

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

Ngược lại : Nó rất đơn giản và ngắn gọn. Không có thư viện bên ngoài chỉ có vani JS

Nhược điểm : Nó có thể sẽ chậm hơn so với việc chỉ lặp lại các giá trị và bạn cần thêm dung lượng.


1

So sánh hai đối tượng bằng ==, ===

Khi sử dụng toán tử ==hoặc ===để so sánh hai đối tượng, bạn sẽ luôn nhận được false trừ khi đối tượng đó tham chiếu đến cùng một đối tượng . Ví dụ:

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

Nếu không, == tương đương với false mặc dù đối tượng chứa các giá trị giống nhau:

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

Bạn có thể cần phải xem xét so sánh thủ công

Trong ECMAScript 6, bạn có thể chuyển đổi bộ thành mảng trước để có thể phát hiện sự khác biệt giữa chúng:

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

LƯU Ý: Array.from là một trong những tính năng ECMAScript 6 tiêu chuẩn nhưng nó không được hỗ trợ rộng rãi trong các trình duyệt hiện đại. Kiểm tra bảng tương thích tại đây: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from#Browser_compatibility


1
Điều này sẽ không xác định thành viên bnào không có trong đó a?

1
@torazaburo Thật vậy. Cách tốt nhất để bỏ qua việc kiểm tra xem các thành viên bkhông tham gia hay không alà kiểm tra xem a.size === b.size.
Aadit M Shah

1
Đặt a.size === b.sizeđầu tiên để ngắn mạch so sánh các phần tử riêng lẻ nếu không cần thiết?

2
Nếu kích thước khác nhau, theo định nghĩa, các bộ không bằng nhau, vì vậy tốt hơn nên kiểm tra điều kiện đó trước.

1
Chà, vấn đề khác ở đây là về bản chất của tập hợp, hashoạt động trên tập hợp được thiết kế để rất hiệu quả, không giống như indexOfhoạt động trên mảng. Do đó, bạn nên thay đổi chức năng bộ lọc của mình return !b.has(i). Điều đó cũng sẽ loại bỏ sự cần thiết phải chuyển đổi bthành một mảng.

1

Tôi đã tạo một polyfill nhanh cho Set.prototype.isEqual ()

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

Dựa trên câu trả lời được chấp nhận, giả sử hỗ trợ Array.from, đây là một lớp lót:

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

Hoặc một dấu lót thực sự giả định các hàm mũi tên và toán tử spread: eqSet = (a,b) => a.size === b.size && [...a].every(b.has.bind(b))
John Hoffer

1

Nếu các tập hợp chỉ chứa các kiểu dữ liệu nguyên thủy hoặc đối tượng bên trong các tập hợp có tham chiếu bình đẳng, thì có một cách đơn giản hơn

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


0

Tôi làm theo cách tiếp cận này trong các thử nghiệm:

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
Chờ một chút ... điều này chỉ kiểm tra xem A có các phần tử mà B không có? Nó không kiểm tra xem B có các phần tử mà A không. Nếu bạn thử a=[1,2,3]b=[1,2,3,4], thì nó cho biết chúng giống nhau. Vì vậy, tôi đoán bạn cần kiểm tra thêm nhưsetA.size === setB.size

0

Sửa đổi rất nhỏ dựa trên câu trả lời của @Aadit M Shah:

/**
 * 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
);

Nếu bất kỳ ai khác đang gặp vấn đề như tôi đã làm do một số sai lầm của babel mới nhất, phải thêm một điều kiện rõ ràng ở đây.

(Ngoài ra đối với số nhiều, tôi nghĩ aređọc to sẽ trực quan hơn một chút 🙃)


-1

1) Kiểm tra xem các kích thước có bằng nhau không. Nếu không, thì chúng không bằng nhau.

2) lặp lại từng điểm của A và kiểm tra xem có tồn tại trong B. Nếu không, hãy quay lại unequal

3) Nếu 2 điều kiện trên không thành công có nghĩa là chúng bằng nhau.

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) Phương pháp 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);


Câu trả lời này hoàn toàn không chính xác. Câu lệnh return trong forEachphương thức sẽ KHÔNG làm cho hàm cha trả về.
xaviert
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.