ECMA6 세트의 동등성 비교


102

두 개의 자바 스크립트 세트를 어떻게 비교합니까? 나는 사용하여 시도 ==하고 ===모두 반환 거짓을하지만.

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

이 두 세트는 정의에 따라 순서가 없기 때문에 동등 합니다 (적어도 보통은 아님). Set on MDN대한 문서를 살펴본 결과 유용한 것이 없습니다. 누구든지 이것을하는 방법을 알고 있습니까?


두 세트는 두 개의 다른 개체입니다. ===객체 평등이 아니라 가치 평등을위한 것입니다.
elclanrs

3
모두 같은, 세트 인 경우 반복 처리하고, 각 멤버의 값을 비교 "같은"
dandavis

1
@dandavis 세트를 사용하면 구성원 값입니다.

2
세트와 맵에는 어떤 이유로 든 삽입 주문 인 주문이 있습니다. developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
CodeManX

8
무엇보다도 new Set([1,2,3]) != new Set([1,2,3]). 이렇게하면 상위 집합에 중복 하위 집합이 포함되므로 집합 집합에 대해 Javascript 집합이 쓸모 없게 됩니다. 떠오르는 유일한 해결 방법은 모든 하위 집합을 배열로 변환하고 각 배열을 정렬 한 다음 각 배열을 문자열 (예 : JSON)로 인코딩하는 것입니다.
7vujy0f0hy

답변:


73

이 시도:

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

보다 기능적인 접근 방식은 다음과 같습니다.

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

all함수는 모든 반복 가능한 객체 (예 : SetMap)에 대해 작동합니다 .

경우 Array.from더 널리 후 지원 우리가 구현 한 수있는 all기능을 같이

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

도움이 되었기를 바랍니다.


2
나는 당신의 이름을 변경해야한다고 생각 hasisPartOf하거나 isIn또는elem
BERGI

@Bergi 함수의 이름 hasisIn.
Aadit M Shah

1
@DavidGiven 예, JavaScript의 세트는 삽입 순서대로 반복됩니다. developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Aadit M Shah

48
매일 나는 JavaScript가 지금까지 만들어진 언어 중 가장 형편없는 언어라고 확신합니다. 한계에 대처하기 위해 누구나 자신의 기본 기능을 발명해야합니다. 이것은 ES6에 있으며 2017 년에 있습니다! 왜 그들은 Set 객체의 사양에 그렇게 빈번한 기능을 추가 할 수 없었습니까!
Ghasan

2
GhasanAl-Sakkaf @, 동의, 그것은 TC39는 과학자들로 구성하는 것이 어쩌면이지만, 더 실용 주의자 없습니다 ...
Marecky

59

다음을 시도해 볼 수도 있습니다.

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


Definitly 그것으로 더 나은 솔루션은 경우 조건 내부에 맞는
Hugodby

이 솔루션이 얼마나 관용적이고 가독성이 높은지 정말 좋아합니다! 감사합니다 @Max
daydreamer

29

lodash_.isEqual()깊은 비교를 수행 하는를 제공합니다 . 직접 작성하고 싶지 않은 경우 매우 편리합니다. lodash 4부터는 _.isEqual()세트를 제대로 비교합니다.

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

다른 대답은 잘 작동합니다. 여기에 또 다른 대안이 있습니다.

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

그러나 이것은 깊은 동등성 비교를 수행 하지 않습니다 . 그래서

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

거짓을 반환합니다. 위의 두 세트가 동일한 것으로 간주되는 경우 각 요소에 대해 심층적 인 품질 비교를 수행하여 두 세트를 반복해야합니다. 우리는 deepEqual일상 의 존재를 규정합니다 . 그러면 논리는

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

이것이하는 일 : s1의 각 구성원에 대해 s2의 동일한 구성원을 찾습니다. 발견되면 다시 사용할 수 없도록 삭제하십시오. s1의 모든 요소가 s2에서 발견 되고 s2가 소진되면 두 세트는 매우 동일합니다 . 테스트되지 않았습니다.

http://www.2ality.com/2015/01/es6-set-operations.html에서 유용 할 수 있습니다 .


6

이러한 솔루션 중 어느 것도 세트 세트 와 같은 데이터 구조에 예상되는 기능을 "복귀"하지 않습니다 . 현재 상태에서 Javascript 세트 는이 목적에 쓸모가 없습니다. 수퍼 세트에 중복 서브 세트가 포함되어 Javascript가 고유 한 것으로 잘못 인식하기 때문입니다. 내가 생각할 수있는 유일한 해결책은 각 하위 집합을 Array 로 변환 하고 정렬 한 다음 문자열 (예 : JSON) 로 인코딩하는 것 입니다.

해결책

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

기본 사용법

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>

궁극적 인 테스트 : 세트 세트

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
훌륭한 솔루션! 당신이 문자열이나 숫자의 집합을 가지고 단지 한 것을 알고 있다면, 그냥이된다[...set1].sort().toString() === [...set2].sort().toString()

3

접근 방식이 false를 반환하는 이유는 두 개의 다른 객체를 비교하기 때문입니다 (동일한 내용을 가지고 있더라도). 따라서 두 개의 다른 객체 (참조가 아니라 객체)를 비교하면 항상 거짓이 반환됩니다.

다음 접근 방식은 두 세트를 하나로 병합하고 크기를 어리석게 비교합니다. 동일하면 동일합니다.

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

장점 : 매우 간단하고 짧습니다. 바닐라 JS 전용 외부 라이브러리 없음

단점 : 값을 반복하는 것보다 느릴 것이고 더 많은 공간이 필요합니다.


1

==, ===를 사용하여 두 개체 비교

==또는 ===연산자를 사용하여 두 개체를 비교할 때 false 해당 개체가 동일한 개체를 참조하지 않는 한 항상 가져옵니다 . 예를 들면 :

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

그렇지 않으면 객체에 동일한 값이 포함되어 있어도 ==는 false와 같습니다.

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

수동 비교를 고려해야 할 수도 있습니다.

ECMAScript 6에서는 미리 집합을 배열로 변환하여 차이점을 찾을 수 있습니다.

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

참고 : Array.from 는 표준 ECMAScript 6 기능 중 하나이지만 최신 브라우저에서는 널리 지원되지 않습니다. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from#Browser_compatibility 에서 호환성 표를 확인하십시오.


1
회원 b이 아닌 회원을 식별하는 데 실패 하지 a않습니까?

1
@torazaburo 맞아요. 가장 좋은 방법은의 회원 여부를 확인 건너 뛸 b에없는 a여부를 확인하는 것입니다 a.size === b.size.
Aadit M Shah 2015

1
넣을 a.size === b.size필요하지 않으면, 단락에 제 개별 요소의 비교?

2
크기가 다른 경우 정의에 따라 세트가 동일하지 않으므로 먼저 해당 조건을 확인하는 것이 좋습니다.

1
음, 여기서 또 다른 문제는 세트의 특성상 세트에 대한 has작업이 indexOf어레이에 대한 작업 과 달리 매우 효율적으로 설계된다는 것 입니다. 따라서 필터 기능을로 변경하는 것이 return !b.has(i)좋습니다. 또한 b배열 로 변환 할 필요가 없습니다 .

1

Set.prototype.isEqual ()에 대한 빠른 polyfill을 만들었습니다.

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

에 대한 지원을 가정하고 허용되는 답변에 따라 Array.from다음은 한 줄짜리입니다.

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

: 또는 진정한 한 줄은 화살표 기능과 확산 운영자 가정 eqSet = (a,b) => a.size === b.size && [...a].every(b.has.bind(b))
존 호퍼

1

세트에 원시 데이터 유형 만 포함되어 있거나 세트 내의 객체가 참조 동등성을 갖는 경우 더 간단한 방법이 있습니다.

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


0

테스트에서이 접근 방식을 따릅니다.

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
잠깐만 ... 이것은 A에 B가없는 요소가 있는지 여부 만 확인합니다. B에 A가없는 요소가 있는지 여부는 확인하지 않습니다. a=[1,2,3]및 을 시도 b=[1,2,3,4]하면 동일하다고 표시됩니다. 내가 추측 그래서 당신은 같은 추가 검사 필요setA.size === setB.size

0

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

다른 사람이 최신 바벨의 기이함으로 인해 내가 한 것처럼 문제가있는 경우 여기에 명시적인 조건을 추가해야합니다.

(또한 복수의 경우 are큰 소리로 읽는 것이 조금 더 직관적 이라고 생각합니다 🙃)


-1

1) 크기가 같은지 확인하십시오. 그렇지 않으면 같지 않습니다.

2) A의 각 요소를 반복하고 B에있는 요소를 확인합니다. 하나가 실패하면 반환 unequal

3) 위의 두 조건이 실패하면 동일하다는 의미입니다.

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


이 대답은 완전히 틀 렸습니다. forEach메서드 의 return 문 은 부모 함수를 반환하지 않습니다.
xaviert 2019-04-15
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.