web-dev-qa-db-fra.com

comparer les ensembles ECMA6 pour l'égalité

Comment comparez-vous deux ensembles javascript? J'ai essayé d'utiliser == et === mais les deux renvoient false.

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

Ces deux ensembles sont équivalents car, par définition, les ensembles n'ont pas ordre (du moins généralement pas). J'ai consulté la documentation de Set on MDN et n'ai rien trouvé d'utile. Quelqu'un sait-il comment faire ça?

62
williamcodes

Essaye ça:

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

Une approche plus fonctionnelle serait:

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

La fonction all fonctionne pour tous les objets itérables (par exemple, Set et Map).

Si Array.from était plus largement supporté, nous aurions pu implémenter la fonction all en tant que:

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

J'espère que cela pourra aider.

41
Aadit M Shah

Vous pouvez aussi essayer:

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

23
Max Leizerovich

lodash fournit _.isEqual(), qui effectue des comparaisons approfondies. Ceci est très pratique si vous ne voulez pas écrire le vôtre. À partir de Lodash 4, _.isEqual() compare correctement les ensembles.

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
18
anthonyserious

L'autre réponse fonctionnera bien; voici une autre alternative.

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

Cependant, sachez que ceci ne fait pas pas une comparaison d’égalité profonde. Alors

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

retournera faux. Si les deux ensembles ci-dessus doivent être considérés comme égaux, nous devons parcourir les deux ensembles en faisant des comparaisons approfondies de la qualité de chaque élément. Nous stipulons l'existence d'une routine deepEqual. Alors la logique serait

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

Ce que cela fait: pour chaque membre de s1, recherchez un membre profondément égal de s2. Si trouvé, supprimez-le afin qu'il ne puisse plus être utilisé. Les deux ensembles sont profondément égaux si tous les éléments de s1 se trouvent dans s2, et s2 est épuisé. Non testé.

Vous pouvez trouver cela utile: http://www.2ality.com/2015/01/es6-set-operations.html .

7
user663031

Aucune de ces solutions ne ramène la fonctionnalité attendue à une structure de données telle que set of sets. Dans son état actuel, le code Javascript Set est inutile à cet effet car le sur-ensemble contient des sous-ensembles dupliqués, que Javascript considère à tort comme distincts. La seule solution à laquelle je puisse penser est de convertir chaque sous-ensemble en Array , de le trier puis de le coder sous la forme String (par exemple, JSON).

Solution

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

Utilisation de base

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>

Test ultime: ensemble d'ensembles

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>

6
7vujy0f0hy

Je suis cette approche dans les tests:

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

Comparaison de deux objets avec ==, ===

Lorsque vous utilisez l'opérateur == ou === pour comparer deux objets, vous obtenez toujours falsesauf si ces objets font référence au même objet . Par exemple:

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

Sinon, == équivaut à false même si l'objet contient les mêmes valeurs:

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

Vous devrez peut-être envisager une comparaison manuelle

Dans ECMAScript 6, vous pouvez convertir les ensembles en tableaux à l’avance afin de repérer la différence entre eux:

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

REMARQUE:Array.from est l’une des fonctionnalités standard d’ECMAScript 6, mais il n’est pas largement pris en charge par les navigateurs modernes. Vérifiez le tableau de compatibilité ici: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from#Browser_compatibility }

2
TaoPR

J'ai créé un polyfill rapide pour 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
Lenny

Sur la base de la réponse acceptée, en supposant que Array.from soit supporté, voici un one-liner:

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

Cette approche fusionne deux ensembles en un seul et compare stupidement la taille. Si c'est pareil, c'est pareil:

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

pside: C'est très simple.

Inconvénient: Il sera probablement plus lent que de simplement parcourir les valeurs et vous aurez besoin de plus d'espace.

0
thadeuszlay

Très légère modification basée sur la réponse de @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
);

Si quelqu'un d'autre a un problème, comme je l'ai eu à cause d'un caprice du dernier babel, il a fallu ajouter une condition explicite ici.

(Également au pluriel, je pense que are est un peu plus intuitif à lire à voix haute ????)

0
rob2d

1) Vérifiez si les tailles sont égales. Sinon, ils ne sont pas égaux.

2) itérer sur chaque élément de A et archiver celui qui existe dans B. En cas d'échec, retourner unequal

3) Si les 2 conditions ci-dessus échouent, cela signifie qu'elles sont égales.

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) Méthode 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);

0
sapy