Comment faire la différence entre deux tableaux en JavaScript?

755

Existe-t-il un moyen de renvoyer la différence entre deux tableaux en JavaScript?

Par exemple:

var a1 = ['a', 'b'];
var a2 = ['a', 'b', 'c', 'd'];

// need ["c", "d"]
John Adawan
la source
9
Symétrique ou non symétrique?
Courses de légèreté en orbite le
Avec la nouvelle fonction ES6, cela peut être fait comme un simple liner (cela prendra beaucoup de temps pour pouvoir être utilisé dans tous les principaux navigateurs). Dans tous les cas, vérifiez ma réponse
Salvador Dali
1
un aspect important de la solution est la performance. la complexité temporelle asymptotique de ce type d'opération - dans d'autres langues - est O(a1.length x log(a2.length))- cette performance est-elle possible en JavaScript?
Raul

Réponses:

219

Je suppose que vous comparez un tableau normal. Sinon, vous devez remplacer la boucle for par une boucle for .. in .

function arr_diff (a1, a2) {

    var a = [], diff = [];

    for (var i = 0; i < a1.length; i++) {
        a[a1[i]] = true;
    }

    for (var i = 0; i < a2.length; i++) {
        if (a[a2[i]]) {
            delete a[a2[i]];
        } else {
            a[a2[i]] = true;
        }
    }

    for (var k in a) {
        diff.push(k);
    }

    return diff;
}

console.log(arr_diff(['a', 'b'], ['a', 'b', 'c', 'd']));
console.log(arr_diff("abcd", "abcde"));
console.log(arr_diff("zxc", "zxc"));

Une meilleure solution, si vous ne vous souciez pas de la compatibilité descendante, utilise le filtre. Mais encore, cette solution fonctionne.

Penseur
la source
46
Cela peut fonctionner mais il fait trois boucles pour accomplir ce qui peut être fait dans une ligne de code en utilisant la méthode de filtrage d'Array.
Joshaven Potter
9
Juste pour être clair, cela implémente la différence symétrique de a1 et a2 , contrairement aux autres réponses publiées ici.
200_success
25
Ce n'est pas la meilleure réponse, mais je lui donne un vote de bienfaisance pour aider à compenser les votes négatifs injustes. Seules les mauvaises réponses devraient être rejetées, et si je travaillais sur un projet avec des navigateurs cruels dans la portée (des moments difficiles se produisent), cette réponse pourrait même être utile.
Michael Scheper
3
Puis-je savoir ce qui se passera quand var a1 = ['a', 'b'];et var a2 = ['a', 'b', 'c', 'd', 'b'];, cela retournera une mauvaise réponse , c'est-à-dire ['c', 'd', 'b']au lieu de ['c', 'd'].
skbly7
4
Le moyen le plus rapide est la solution la plus évidemment naïve. J'ai testé toutes les solutions proposées pour les différences symétriques dans ce fil, et le gagnant est:function diff2(a, b) { var i, la = a.length, lb = b.length, res = []; if (!la) return b; else if (!lb) return a; for (i = 0; i < la; i++) { if (b.indexOf(a[i]) === -1) res.push(a[i]); } for (i = 0; i < lb; i++) { if (a.indexOf(b[i]) === -1) res.push(b[i]); } return res; }
Nomaed
1236

Il existe une meilleure façon d'utiliser ES7:


Intersection

 let intersection = arr1.filter(x => arr2.includes(x));

Différence d'intersection Diagramme de Venn

Car [1,2,3] [2,3]cela cédera [2,3]. D'un autre côté, for [1,2,3] [2,3,5]renverra la même chose.


Différence

let difference = arr1.filter(x => !arr2.includes(x));

Différence droite Diagramme de Venn

Car [1,2,3] [2,3]cela cédera [1]. D'un autre côté, for [1,2,3] [2,3,5]renverra la même chose.


Pour une différence symétrique , vous pouvez faire:

let difference = arr1
                 .filter(x => !arr2.includes(x))
                 .concat(arr2.filter(x => !arr1.includes(x)));

Diagramme de Venn à différence symétrique

De cette façon, vous obtiendrez un tableau contenant tous les éléments de arr1 qui ne sont pas dans arr2 et vice-versa

Comme @Joshaven Potter l'a souligné dans sa réponse, vous pouvez l'ajouter à Array.prototype afin qu'il puisse être utilisé comme ceci:

Array.prototype.diff = function(arr2) { return this.filter(x => !arr2.includes(x)); }
[1, 2, 3].diff([2, 3])
Luis Sieira
la source
3
Je préfère vérifier < 0plutôt que== -1
Vic
1
Le calcul de la Arraydifférence est un soi-disant set operation, car la recherche de propriété est le propre travail de Sets, qui sont des ordres de grandeur plus rapides que indexOf/ includes. Autrement dit, votre solution est très inefficace et plutôt lente.
@ftor mais avec Set, les valeurs doivent être uniques, non?
CervEd
1
@LuisSieira Je comprends que cela fonctionnerait car [1,2,3] [2,3,5]les chiffres sont uniques, mais si vous aviez dit [1,1,2,3] [1,2,3,5]et attendu que [1]vous ne pouviez pas utiliser Set. Votre solution ne fonctionnerait pas non plus: - / J'ai fini par créer cette fonction parce que je ne pouvais pas trouver une manière satisfaisante de le faire de manière plus succincte. Si vous avez des idées sur la façon de le faire, j'aimerais savoir!
CervEd
3
La Array.includes()fonctionnalité ES7 n'est-elle pas au lieu de ES6? (1) (2) - et pour continuer, avec ES6, vous pourriez Array.some()par exemple utiliser let intersection = aArray.filter(a => bArray.some(b => a === b))non?
Jari Keinänen
910
Array.prototype.diff = function(a) {
    return this.filter(function(i) {return a.indexOf(i) < 0;});
};

////////////////////  
// Examples  
////////////////////

[1,2,3,4,5,6].diff( [3,4,5] );  
// => [1, 2, 6]

["test1", "test2","test3","test4","test5","test6"].diff(["test1","test2","test3","test4"]);  
// => ["test5", "test6"]

Remarque indexOf et filtre ne sont pas disponibles dans ie avant ie9.

Joshaven Potter
la source
50
Le seul navigateur important qui ne prend pas en charge le filtre et indexOf est IE8. IE9 les prend en charge tous les deux. Ce n'est donc pas faux.
Bryan Larsen
14
ie7 et ie8 sont toujours (malheureusement) très pertinents, mais vous pouvez trouver du code polyfill pour les deux fonctions sur le site MDN: developer.mozilla.org/en/JavaScript/Reference/Global_Objects/… developer.mozilla.org/en/JavaScript/ Référence / Global_Objects /… Charger le code listé sous "compatibilité" via un IE conditionnel & BOOM. Ie7 / 8 sont pris en charge.
1nfiniti
44
Cette solution a un temps d'exécution de O (n ^ 2), une solution linéaire serait beaucoup plus efficace.
jholloman
75
Si vous utilisez la fonction comme ceci: [1,2,3].diff([3,4,5])elle reviendra [1,2]au lieu de [1,2,4,5]donc elle ne résout pas le problème dans la question d'origine, quelque chose à savoir.
Bugster
12
@AlinPurcaru Non pris en charge par les navigateurs archaïques! = Faux. En considérant Netscape 2.0, la plupart du code JS ici est "faux" selon cette définition. C'est une chose idiote à dire.
NullUserException
304

C'est de loin le moyen le plus simple d'obtenir exactement le résultat que vous recherchez, en utilisant jQuery:

var diff = $(old_array).not(new_array).get();

diffcontient maintenant ce qui était dans old_arrayce n'est pasnew_array

superphonique
la source
4
@Batman Oui, mais seulement s'il s'agit de références au même objet ( {a: 1} != {a: 1}) ( preuve )
Matmarbon
Est-il possible de l'utiliser avec des tableaux contenant des données d'un objet personnalisé? Je l'ai essayé en fait de la même manière mais cela n'a pas fonctionné. Toutes les idées seront très appréciables.
LetMeCodeYou
8
Est-ce une astuce? Le doc considère cette méthode comme faisant partie des méthodes des éléments DOM et non comme une aide générale de tableau. Donc, cela pourrait fonctionner de cette façon maintenant, mais peut-être pas dans les versions futures, car il n'était pas prévu de l'utiliser de cette façon. Cependant, je serais heureux que ce soit officiellement un assistant général.
robsch
1
@robsch Lorsque vous utilisez .notavec un tableau, jQuery utilise son utilitaire intégré .grep()qui est spécifiquement destiné au filtrage des tableaux. Je ne vois pas cela changer.
2015 superphonique
1
@vsync On dirait que vous êtes après une différence symétrique
superphonique
158

La méthode de différence dans Underscore (ou son remplacement direct , Lo-Dash ) peut également faire cela:

(R)eturns the values from array that are not present in the other arrays

_.difference([1, 2, 3, 4, 5], [5, 2, 10]);
=> [1, 3, 4]

Comme avec n'importe quelle fonction Underscore, vous pouvez également l'utiliser dans un style plus orienté objet:

_([1, 2, 3, 4, 5]).difference([5, 2, 10]);
mahemoff
la source
4
Je pense que c'est une bonne solution en termes de performances, d'autant que lodash et underscore continuent de se battre pour la meilleure implémentation. Il est également compatible avec IE6.
mahemoff
4
Attention, cette implémentation ne fonctionnera pas pour les tableaux d'objets. Voir stackoverflow.com/q/8672383/14731 pour plus d'informations.
Gili
1
Comme l'une des réponses le mentionne, cela fonctionne si c'est le même objet, mais pas si deux objets ont les mêmes propriétés. Je pense que c'est correct car les notions d'égalité varient (par exemple, cela pourrait également être un attribut "id" dans certaines applications). Cependant, ce serait bien si vous pouviez réussir un test de comparaison pour intersecter ().
mahemoff
Pour la postérité: Lodash a maintenant _.differenceBy () qui prend un rappel pour faire la comparaison; si vous comparez des objets, vous pouvez insérer une fonction qui les compare comme vous le souhaitez.
SomeCallMeTim
2
Soyez prudent si l'ordre des arguments est inversé, cela ne fonctionnera pas. par exemple. _différence ([5, 2, 10], [1, 2, 3, 4, 5]); ne peut pas obtenir le diff
Russj
79

JavaScript simple

Il existe deux interprétations possibles de "différence". Je vous laisse choisir celui que vous voulez. Dites que vous avez:

var a1 = ['a', 'b'     ];
var a2 = [     'b', 'c'];
  1. Si vous voulez l'obtenir ['a'], utilisez cette fonction:

    function difference(a1, a2) {
      var result = [];
      for (var i = 0; i < a1.length; i++) {
        if (a2.indexOf(a1[i]) === -1) {
          result.push(a1[i]);
        }
      }
      return result;
    }
  2. Si vous voulez obtenir ['a', 'c'](tous les éléments contenus dans l' un a1 ou l' autrea2 , mais pas les deux - la soi-disant différence symétrique ), utilisez cette fonction:

    function symmetricDifference(a1, a2) {
      var result = [];
      for (var i = 0; i < a1.length; i++) {
        if (a2.indexOf(a1[i]) === -1) {
          result.push(a1[i]);
        }
      }
      for (i = 0; i < a2.length; i++) {
        if (a1.indexOf(a2[i]) === -1) {
          result.push(a2[i]);
        }
      }
      return result;
    }

Lodash / Underscore

Si vous utilisez lodash, vous pouvez utiliser _.difference(a1, a2)(cas 1 ci-dessus) ou _.xor(a1, a2)(cas 2).

Si vous utilisez Underscore.js, vous pouvez utiliser la _.difference(a1, a2)fonction pour le cas 1.

Set ES6, pour très grandes baies

Le code ci-dessus fonctionne sur tous les navigateurs. Cependant, pour les grands tableaux de plus d'environ 10 000 éléments, cela devient assez lent, car il a une complexité O (n²). Sur de nombreux navigateurs modernes, nous pouvons profiter de l' Setobjet ES6 pour accélérer les choses. Lodash utilise automatiquement Setlorsqu'il est disponible. Si vous n'utilisez pas lodash, utilisez l'implémentation suivante, inspirée du billet de blog d'Axel Rauschmayer :

function difference(a1, a2) {
  var a2Set = new Set(a2);
  return a1.filter(function(x) { return !a2Set.has(x); });
}

function symmetricDifference(a1, a2) {
  return difference(a1, a2).concat(difference(a2, a1));
}

Remarques

Le comportement de tous les exemples peut être surprenant ou non évident si vous vous souciez des tableaux -0, +0, NaN ou clairsemés. (Pour la plupart des utilisations, cela n'a pas d'importance.)

Jo Liss
la source
Remercier. tu m'as sauvé la journée. J'ai dû comparer un tableau 300K, et votre solution "Set" fonctionnait parfaitement. Ce devrait être la réponse acceptée.
justadev
52

Pour obtenir la différence symétrique, vous devez comparer les tableaux dans les deux sens (ou dans tous les cas en cas de tableaux multiples)

entrez la description de l'image ici


ES7 (ECMAScript 2016)

// diff between just two arrays:
function arrayDiff(a, b) {
    return [
        ...a.filter(x => !b.includes(x)),
        ...b.filter(x => !a.includes(x))
    ];
}

// diff between multiple arrays:
function arrayDiff(...arrays) {
    return [].concat(...arrays.map( (arr, i) => {
        const others = arrays.slice(0);
        others.splice(i, 1);
        const unique = [...new Set([].concat(...others))];
        return arr.filter(x => !unique.includes(x));
    }));
}

ES6 (ECMAScript 2015)

// diff between just two arrays:
function arrayDiff(a, b) {
    return [
        ...a.filter(x => b.indexOf(x) === -1),
        ...b.filter(x => a.indexOf(x) === -1)
    ];
}

// diff between multiple arrays:
function arrayDiff(...arrays) {
    return [].concat(...arrays.map( (arr, i) => {
        const others = arrays.slice(0);
        others.splice(i, 1);
        const unique = [...new Set([].concat(...others))];
        return arr.filter(x => unique.indexOf(x) === -1);
    }));
}

ES5 (ECMAScript 5.1)

// diff between just two arrays:
function arrayDiff(a, b) {
    var arrays = Array.prototype.slice.call(arguments);
    var diff = [];

    arrays.forEach(function(arr, i) {
        var other = i === 1 ? a : b;
        arr.forEach(function(x) {
            if (other.indexOf(x) === -1) {
                diff.push(x);
            }
        });
    })

    return diff;
}

// diff between multiple arrays:
function arrayDiff() {
    var arrays = Array.prototype.slice.call(arguments);
    var diff = [];

    arrays.forEach(function(arr, i) {
        var others = arrays.slice(0);
        others.splice(i, 1);
        var otherValues = Array.prototype.concat.apply([], others);
        var unique = otherValues.filter(function (x, j) { 
            return otherValues.indexOf(x) === j; 
        });
        diff = diff.concat(arr.filter(x => unique.indexOf(x) === -1));
    });
    return diff;
}

Exemple:

// diff between two arrays:
const a = ['a', 'd', 'e'];
const b = ['a', 'b', 'c', 'd'];
arrayDiff(a, b); // (3) ["e", "b", "c"]

// diff between multiple arrays
const a = ['b', 'c', 'd', 'e', 'g'];
const b = ['a', 'b'];
const c = ['a', 'e', 'f'];
arrayDiff(a, b, c); // (4) ["c", "d", "g", "f"]

Différence entre les tableaux d'objets

function arrayDiffByKey(key, ...arrays) {
    return [].concat(...arrays.map( (arr, i) => {
        const others = arrays.slice(0);
        others.splice(i, 1);
        const unique = [...new Set([].concat(...others))];
        return arr.filter( x =>
            !unique.some(y => x[key] === y[key])
        );
    }));
}

Exemple:

const a = [{k:1}, {k:2}, {k:3}];
const b = [{k:1}, {k:4}, {k:5}, {k:6}];
const c = [{k:3}, {k:5}, {k:7}];
arrayDiffByKey('k', a, b, c); // (4) [{k:2}, {k:4}, {k:6}, {k:7}]
Luca Borrione
la source
51

Une approche plus propre dans ES6 est la solution suivante.

var a1 = ['a', 'b'];
var a2 = ['a', 'b', 'c', 'd'];

Différence

a2.filter(d => !a1.includes(d)) // gives ["c", "d"]

Intersection

a2.filter(d => a1.includes(d)) // gives ["a", "b"]

Union disjonctive (différence symétrique)

[ ...a2.filter(d => !a1.includes(d)),
  ...a1.filter(d => !a2.includes(d)) ]
ifelse.codes
la source
Cela ne fonctionne que dans une seule direction. Imaginez maintenant que a1 = ['a', 'b', 'e']: e ne sera pas extrait.
imrok
oui, c'est ainsi que fonctionne la différence dans la théorie des ensembles. (a2 -a1) ce que vous recherchez est (a2-a1) + (a1-a2)
ifelse.codes
1
@imrok Je crois que c'est ce que vous recherchez [... a2.filter (d =>! a1.includes (d)), ... (a1.filter (d =>! a2.includes (d)) )]
ifelse.codes
2
Belle solution, merci!
Thiago Alves
40

Vous pouvez utiliser un ensemble dans ce cas. Il est optimisé pour ce type d'opération (union, intersection, différence).

Assurez-vous qu'il s'applique à votre cas, une fois qu'il n'autorise aucun doublon.

var a = new JS.Set([1,2,3,4,5,6,7,8,9]);
var b = new JS.Set([2,4,6,8]);

a.difference(b)
// -> Set{1,3,5,7,9}
Samuel Carrijo
la source
4
Cela ressemble à une belle bibliothèque! Quel dommage que vous ne puissiez pas télécharger uniquement la Setfonction sans avoir à obtenir tout le reste ...
Blixt
@Blixt Je crois que vous pouvez tout télécharger et n'inclure que le fichier set.js
Samuel Carrijo
L'ensemble est également implémenté dans la fermeture de Google. close-library.googlecode.com/svn/docs/…
Ben Flynn
32
function diff(a1, a2) {
  return a1.concat(a2).filter(function(val, index, arr){
    return arr.indexOf(val) === arr.lastIndexOf(val);
  });
}

Fusionnez les deux tableaux, les valeurs uniques n'apparaîtront qu'une seule fois, donc indexOf () sera le même que lastIndexOf ().

pdbrito
la source
3
Je suis d'accord pour dire que c'est le moyen le plus propre et le plus simple et agréable qu'il ne nécessite pas de toucher le prototype. "Si vous ne pouvez pas l'expliquer à un enfant de six ans, vous ne le comprenez pas vous-même." - Albert Einstein
lacostenycoder
18

pour soustraire un tableau d'un autre, utilisez simplement l'extrait ci-dessous:

var a1 = ['1','2','3','4','6'];
var a2 = ['3','4','5'];

var items = new Array();

items = jQuery.grep(a1,function (item) {
    return jQuery.inArray(item, a2) < 0;
});

Il retournera ['1,' 2 ',' 6 '] qui sont des éléments du premier tableau qui n'existent pas dans le second.

Par conséquent, selon votre exemple de problème, le code suivant est la solution exacte:

var array1 = ["test1", "test2","test3", "test4"];
var array2 = ["test1", "test2","test3","test4", "test5", "test6"];

var _array = new Array();

_array = jQuery.grep(array2, function (item) {
     return jQuery.inArray(item, array1) < 0;
});
Al___
la source
14

Avec l'arrivée d'ES6 avec les sets et l'opérateur splat (au moment de ne fonctionner que dans Firefox, consultez le tableau de compatibilité ), vous pouvez écrire le liner suivant:

var a = ['a', 'b', 'c', 'd'];
var b = ['a', 'b'];
var b1 = new Set(b);
var difference = [...new Set([...a].filter(x => !b1.has(x)))];

qui se traduira par [ "c", "d" ].

Salvador Dali
la source
Juste curieux de savoir comment est-ce différent de faireb.filter(x => !a.indexOf(x)))
chovy
1
@chovy, sa complexité temporelle est différente. Ma solution est que O(n + m)votre solution est O(n * m)où n et m sont des longueurs de tableaux. Prenez de longues listes et ma solution fonctionnera en quelques secondes, tandis que la vôtre prendra des heures.
Salvador Dali
Qu'en est-il de la comparaison d'un attribut d'une liste d'objets? Est-ce possible en utilisant cette solution?
chovy
2
a.filter(x => !b1.has(x))est plus simple. Et notez que la spécification nécessite uniquement que la complexité soit n * f(m) + men f(m)sublinéaire en moyenne. C'est mieux que n * m, mais pas nécessairement n + m.
Oriol
2
@SalvadorDali var difference = [...new Set([...a].filter(x => !b1.has(x)))];Pourquoi créez -vous un tableau 'a' en double? Pourquoi transformez-vous le résultat du filtre en un ensemble, puis de nouveau en tableau? N'est-ce pas l'équivalent devar difference = a.filter(x => !b1.has(x));
Deepak Mittal
13

Approche fonctionnelle avec ES2015

Le calcul differenceentre deux tableaux est l'une des Setopérations. Le terme indique déjà que le Settype natif doit être utilisé, afin d'augmenter la vitesse de recherche. Quoi qu'il en soit, il y a trois permutations lorsque vous calculez la différence entre deux ensembles:

[+left difference] [-intersection] [-right difference]
[-left difference] [-intersection] [+right difference]
[+left difference] [-intersection] [+right difference]

Voici une solution fonctionnelle qui reflète ces permutations.

Gauche difference:

// small, reusable auxiliary functions

const apply = f => x => f(x);
const flip = f => y => x => f(x) (y);
const createSet = xs => new Set(xs);
const filter = f => xs => xs.filter(apply(f));


// left difference

const differencel = xs => ys => {
  const zs = createSet(ys);
  return filter(x => zs.has(x)
     ? false
     : true
  ) (xs);
};


// mock data

const xs = [1,2,2,3,4,5];
const ys = [0,1,2,3,3,3,6,7,8,9];


// run the computation

console.log( differencel(xs) (ys) );

Droite difference :

differencerest trivial. C'est juste differencelavec des arguments inversés. Vous pouvez écrire une fonction pour plus de commodité:const differencer = flip(differencel) . C'est tout!

Symétrique difference:

Maintenant que nous avons celui de gauche et de droite, l'implémentation du symétrique differencedevient également triviale:

// small, reusable auxiliary functions

const apply = f => x => f(x);
const flip = f => y => x => f(x) (y);
const concat = y => xs => xs.concat(y);
const createSet = xs => new Set(xs);
const filter = f => xs => xs.filter(apply(f));


// left difference

const differencel = xs => ys => {
  const zs = createSet(ys);
  return filter(x => zs.has(x)
     ? false
     : true
  ) (xs);
};


// symmetric difference

const difference = ys => xs =>
 concat(differencel(xs) (ys)) (flip(differencel) (xs) (ys));

// mock data

const xs = [1,2,2,3,4,5];
const ys = [0,1,2,3,3,3,6,7,8,9];


// run the computation

console.log( difference(xs) (ys) );

Je suppose que cet exemple est un bon point de départ pour avoir une idée de ce que signifie la programmation fonctionnelle:

Programmation avec des blocs de construction qui peuvent être connectés de différentes manières.


la source
12

Une solution utilisant indexOf() sera correcte pour les petits tableaux mais à mesure qu'ils grandissent, les performances de l'algorithme se rapprochent O(n^2). Voici une solution qui fonctionnera mieux pour les très grands tableaux en utilisant des objets comme tableaux associatifs pour stocker les entrées du tableau sous forme de clés; il élimine également automatiquement les entrées en double mais ne fonctionne qu'avec des valeurs de chaîne (ou des valeurs qui peuvent être stockées en toute sécurité en tant que chaînes):

function arrayDiff(a1, a2) {
  var o1={}, o2={}, diff=[], i, len, k;
  for (i=0, len=a1.length; i<len; i++) { o1[a1[i]] = true; }
  for (i=0, len=a2.length; i<len; i++) { o2[a2[i]] = true; }
  for (k in o1) { if (!(k in o2)) { diff.push(k); } }
  for (k in o2) { if (!(k in o1)) { diff.push(k); } }
  return diff;
}

var a1 = ['a', 'b'];
var a2 = ['a', 'b', 'c', 'd'];
arrayDiff(a1, a2); // => ['c', 'd']
arrayDiff(a2, a1); // => ['c', 'd']
maerics
la source
Vous souhaitez utiliser Object.hasOwnProperty () chaque fois que vous effectuez un "for in" sur un objet. Sinon, vous courez le risque de parcourir tous les champs ajoutés au prototype de l'objet par défaut. (Ou simplement le parent de votre objet) Vous n'avez également besoin que de deux boucles, l'une pour la création d'une table de hachage, et l'autre recherche cette table de hachage.
jholloman
1
@jholloman Je suis respectueusement en désaccord . Maintenant que nous pouvons contrôler l'énumération de n'importe quelle propriété, vous devriez probablement inclure toutes les propriétés que vous obtenez pendant l'énumération.
Phrogz
1
@Phrogz Un bon point si vous ne vous inquiétez que des navigateurs modernes. Malheureusement, je dois revenir sur IE7 au travail, donc l'âge de pierre est ma pensée par défaut et nous n'avons pas tendance à utiliser des cales.
jholloman
10

La réponse ci-dessus de Joshaven Potter est excellente. Mais il renvoie des éléments du tableau B qui ne sont pas dans le tableau C, mais pas l'inverse. Par exemple, si var a=[1,2,3,4,5,6].diff( [3,4,5,7]);alors il affichera: ==> [1,2,6], mais pas [1,2,6,7] , quelle est la différence réelle entre les deux. Vous pouvez toujours utiliser le code de Potter ci-dessus mais simplement refaire la comparaison une fois en arrière aussi:

Array.prototype.diff = function(a) {
    return this.filter(function(i) {return !(a.indexOf(i) > -1);});
};

////////////////////  
// Examples  
////////////////////

var a=[1,2,3,4,5,6].diff( [3,4,5,7]);
var b=[3,4,5,7].diff([1,2,3,4,5,6]);
var c=a.concat(b);
console.log(c);

Cela devrait produire: [ 1, 2, 6, 7 ]

user1685068
la source
9

Une autre façon de résoudre le problème

function diffArray(arr1, arr2) {
    return arr1.concat(arr2).filter(function (val) {
        if (!(arr1.includes(val) && arr2.includes(val)))
            return val;
    });
}

diffArray([1, 2, 3, 7], [3, 2, 1, 4, 5]);    // return [7, 4, 5]

Vous pouvez également utiliser la syntaxe de la fonction flèche:

const diffArray = (arr1, arr2) => arr1.concat(arr2)
    .filter(val => !(arr1.includes(val) && arr2.includes(val)));

diffArray([1, 2, 3, 7], [3, 2, 1, 4, 5]);    // return [7, 4, 5]
Iurii Golskyi
la source
7
Array.prototype.difference = function(e) {
    return this.filter(function(i) {return e.indexOf(i) < 0;});
};

eg:- 

[1,2,3,4,5,6,7].difference( [3,4,5] );  
 => [1, 2, 6 , 7]
Riyas TK
la source
Vous ne devez jamais étendre un objet natif de cette façon. Si la norme introduit differencecomme fonction dans une future version et que cette fonction a alors une signature de fonction différente de la vôtre, elle cassera votre code ou les bibliothèques étrangères qui utilisent cette fonction.
t.niese
7

Solution très simple avec la fonction de filtrage de JavaScript:

var a1 = ['a', 'b'];
var a2 = ['a', 'b', 'c', 'd'];

function diffArray(arr1, arr2) {
  var newArr = [];
  var myArr = arr1.concat(arr2);
  
    newArr = myArr.filter(function(item){
      return arr2.indexOf(item) < 0 || arr1.indexOf(item) < 0;
    });
   alert(newArr);
}

diffArray(a1, a2);

Yash Thakkar
la source
6

Que dis-tu de ça:

Array.prototype.contains = function(needle){
  for (var i=0; i<this.length; i++)
    if (this[i] == needle) return true;

  return false;
} 

Array.prototype.diff = function(compare) {
    return this.filter(function(elem) {return !compare.contains(elem);})
}

var a = new Array(1,4,7, 9);
var b = new Array(4, 8, 7);
alert(a.diff(b));

Donc, de cette façon, vous pouvez faire array1.diff(array2)pour obtenir leur différence (complexité horrible du temps pour l'algorithme - O (array1.length x array2.length) je crois)

Chat
la source
L'utilisation de l'option de filtre est une excellente idée ... cependant, vous n'avez pas besoin de créer une méthode contains pour Array. J'ai converti votre idée en une doublure ... Merci pour l'inspiration!
Joshaven Potter
Vous n'avez pas besoin de définir la fonction contains (). JS includes () fait la même chose.
Da Man
4

En utilisant http://phrogz.net/JS/ArraySetMath.js, vous pouvez:

var array1 = ["test1", "test2","test3", "test4"];
var array2 = ["test1", "test2","test3","test4", "test5", "test6"];

var array3 = array2.subtract( array1 );
// ["test5", "test6"]

var array4 = array1.exclusion( array2 );
// ["test5", "test6"]
Phrogz
la source
4
function diffArray(arr1, arr2) {
  var newArr = arr1.concat(arr2);
  return newArr.filter(function(i){
    return newArr.indexOf(i) == newArr.lastIndexOf(i);
  });
}

cela fonctionne pour moi

Supporter
la source
3
  • Solution JavaScript pure (pas de bibliothèques)
  • Compatible avec les anciens navigateurs (n'utilise pas filter )
  • O (n ^ 2)
  • fnParamètre de rappel facultatif qui vous permet de spécifier comment comparer les éléments du tableau

function diff(a, b, fn){
    var max = Math.max(a.length, b.length);
        d = [];
    fn = typeof fn === 'function' ? fn : false
    for(var i=0; i < max; i++){
        var ac = i < a.length ? a[i] : undefined
            bc = i < b.length ? b[i] : undefined;
        for(var k=0; k < max; k++){
            ac = ac === undefined || (k < b.length && (fn ? fn(ac, b[k]) : ac == b[k])) ? undefined : ac;
            bc = bc === undefined || (k < a.length && (fn ? fn(bc, a[k]) : bc == a[k])) ? undefined : bc;
            if(ac == undefined && bc == undefined) break;
        }
        ac !== undefined && d.push(ac);
        bc !== undefined && d.push(bc);
    }
    return d;
}

alert(
    "Test 1: " + 
    diff(
        [1, 2, 3, 4],
        [1, 4, 5, 6, 7]
      ).join(', ') +
    "\nTest 2: " +
    diff(
        [{id:'a',toString:function(){return this.id}},{id:'b',toString:function(){return this.id}},{id:'c',toString:function(){return this.id}},{id:'d',toString:function(){return this.id}}],
        [{id:'a',toString:function(){return this.id}},{id:'e',toString:function(){return this.id}},{id:'f',toString:function(){return this.id}},{id:'d',toString:function(){return this.id}}],
        function(a, b){ return a.id == b.id; }
    ).join(', ')
);

Trevor
la source
Vous pouvez mettre en cache des valeurs de longueur pour augmenter la vitesse. Je voulais recommander d'accéder aux éléments du tableau sans vérifier la longueur, mais apparemment, cette simple vérification donne une accélération de presque 100x.
Slotos
Aucune raison de mettre en cache les lengthvaleurs. C'est déjà une propriété ordinaire. jsperf.com/array-length-caching
vp_arth
3

Cela fonctionne: fusionnez essentiellement les deux tableaux, recherchez les doublons et placez ce qui n'est pas dupliqué dans un nouveau tableau, ce qui fait la différence.

function diff(arr1, arr2) {
  var newArr = [];
  var arr = arr1.concat(arr2);
  
  for (var i in arr){
    var f = arr[i];
    var t = 0;
    for (j=0; j<arr.length; j++){
      if(arr[j] === f){
        t++; 
        }
    }
    if (t === 1){
      newArr.push(f);
        }
  } 
  return newArr;
}

Giorgio Giuliani
la source
3

// approche es6

function diff(a, b) {
  var u = a.slice(); //dup the array
  b.map(e => {
    if (u.indexOf(e) > -1) delete u[u.indexOf(e)]
    else u.push(e)   //add non existing item to temp array
  })
  return u.filter((x) => {return (x != null)}) //flatten result
}
coprémèse
la source
3

Complexité symétrique et linéaire . Nécessite ES6.

function arrDiff(arr1, arr2) {
    var arrays = [arr1, arr2].sort((a, b) => a.length - b.length);
    var smallSet = new Set(arrays[0]);

    return arrays[1].filter(x => !smallSet.has(x));
}
Dodgie
la source
3

encore une autre réponse, mais il semble que personne n'ait mentionné jsperf où ils comparent plusieurs algorithmes et support technologique: https://jsperf.com/array-difference-javascript semble utiliser le filtre pour obtenir les meilleurs résultats. Merci

cancerbero
la source
2

Penser juste ... pour le plaisir ;-) est-ce que cela fonctionnerait ... (pour les tableaux de base de chaînes, de nombres, etc.) pas de tableaux imbriqués

function diffArrays(arr1, arr2, returnUnion){
  var ret = [];
  var test = {};
  var bigArray, smallArray, key;
  if(arr1.length >= arr2.length){
    bigArray = arr1;
    smallArray = arr2;
  } else {
    bigArray = arr2;
    smallArray = arr1;
  }
  for(var i=0;i<bigArray.length;i++){
    key = bigArray[i];
    test[key] = true;
  }
  if(!returnUnion){
    //diffing
    for(var i=0;i<smallArray.length;i++){
      key = smallArray[i];
      if(!test[key]){
        test[key] = null;
      }
    }
  } else {
    //union
    for(var i=0;i<smallArray.length;i++){
      key = smallArray[i];
      if(!test[key]){
        test[key] = true;
      }
    }
  }
  for(var i in test){
    ret.push(i);
  }
  return ret;
}

array1 = "test1", "test2","test3", "test4", "test7"
array2 = "test1", "test2","test3","test4", "test5", "test6"
diffArray = diffArrays(array1, array2);
//returns ["test5","test6","test7"]

diffArray = diffArrays(array1, array2, true);
//returns ["test1", "test2","test3","test4", "test5", "test6","test7"]

Notez que le tri ne sera probablement pas comme indiqué ci-dessus ... mais si vous le souhaitez, appelez .sort () sur le tableau pour le trier.

scunliffe
la source
2

Je voulais une fonction similaire qui prenait un ancien tableau et un nouveau tableau et me donnait un tableau d'éléments ajoutés et un tableau d'éléments supprimés, et je voulais qu'elle soit efficace (donc pas de .contains!).

Vous pouvez jouer avec ma solution proposée ici: http://jsbin.com/osewu3/12 .

Quelqu'un peut-il voir des problèmes / améliorations à cet algorithme? Merci!

Liste des codes:

function diff(o, n) {
  // deal with empty lists
  if (o == undefined) o = [];
  if (n == undefined) n = [];

  // sort both arrays (or this won't work)
  o.sort(); n.sort();

  // don't compare if either list is empty
  if (o.length == 0 || n.length == 0) return {added: n, removed: o};

  // declare temporary variables
  var op = 0; var np = 0;
  var a = []; var r = [];

  // compare arrays and add to add or remove lists
  while (op < o.length && np < n.length) {
      if (o[op] < n[np]) {
          // push to diff?
          r.push(o[op]);
          op++;
      }
      else if (o[op] > n[np]) {
          // push to diff?
          a.push(n[np]);
          np++;
      }
      else {
          op++;np++;
      }
  }

  // add remaining items
  if( np < n.length )
    a = a.concat(n.slice(np, n.length));
  if( op < o.length )
    r = r.concat(o.slice(op, o.length));

  return {added: a, removed: r}; 
}
Ian Grainger
la source
2

Je cherchais une réponse simple qui n'impliquait pas l'utilisation de différentes bibliothèques, et j'ai trouvé la mienne qui, je pense, n'a pas été mentionnée ici. Je ne sais pas à quel point c'est efficace ou quoi que ce soit mais ça marche;

    function find_diff(arr1, arr2) {
      diff = [];
      joined = arr1.concat(arr2);
      for( i = 0; i <= joined.length; i++ ) {
        current = joined[i];
        if( joined.indexOf(current) == joined.lastIndexOf(current) ) {
          diff.push(current);
        }
      }
      return diff;
    }

Pour mon code, j'ai également besoin de doublons, mais je suppose que ce n'est pas toujours préféré.

Je suppose que le principal inconvénient est qu'il compare potentiellement de nombreuses options qui ont déjà été rejetées.

Samuel
la source
2

correction de petit bit pour la meilleure réponse

function arr_diff(a1, a2)
{
  var a=[], diff=[];
  for(var i=0;i<a1.length;i++)
    a[a1[i]]=a1[i];
  for(var i=0;i<a2.length;i++)
    if(a[a2[i]]) delete a[a2[i]];
    else a[a2[i]]=a2[i];
  for(var k in a)
   diff.push(a[k]);
  return diff;
}

cela prendra en considération le type d'élément actuel. b / c lorsque nous créons un [a1 [i]], il convertit une valeur en chaîne à partir de sa valeur d'origine, nous avons donc perdu la valeur réelle.

Vedanta-krit das
la source
Cela échoue toujours pour un tableau d'objets. var a = [{a: 1}], b = [{b: 2}] arr_diff (a, b) == [].
EricP