Come ottenere la differenza tra due array in JavaScript?


754

C'è un modo per restituire la differenza tra due array in JavaScript?

Per esempio:

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

// need ["c", "d"]

9
Simmetrico o non simmetrico?
Corse di leggerezza in orbita

Con la nuova funzione ES6 questo può essere fatto come un semplice liner (ci vorrà molto tempo per poterlo usare in tutti i principali browser). In ogni caso, controlla la mia risposta
Salvador Dali,

1
un aspetto importante della soluzione è la prestazione. la complessità temporale asintotica di questo tipo di operazione - in altre lingue - è O(a1.length x log(a2.length))- questa prestazione è possibile in JavaScript?
Raul,

Risposte:


219

Suppongo che stai confrontando un array normale. In caso contrario, è necessario modificare il ciclo for in un ciclo 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"));

Una soluzione migliore, se non ti interessa la compatibilità con le versioni precedenti, sta usando il filtro. Tuttavia, questa soluzione funziona.


46
Questo può funzionare ma fa tre cicli per realizzare ciò che può essere fatto in una riga di codice usando il metodo di filtro di Array.
Joshaven Potter,

9
Per essere chiari, questo implementa la differenza simmetrica di a1 e a2 , a differenza delle altre risposte pubblicate qui.
200_successo

25
Questa non è la risposta migliore, ma le sto dando un voto di beneficenza, per aiutare a compensare gli ingiusti voti negativi. Solo le risposte sbagliate dovrebbero essere sottovalutate, e se stavo lavorando a un progetto con browser di cruft nell'ambito (tempi difficili), questa risposta potrebbe anche essere utile.
Michael Scheper,

3
Posso sapere cosa accadrà quando var a1 = ['a', 'b'];e var a2 = ['a', 'b', 'c', 'd', 'b'];, restituirà una risposta sbagliata , cioè ['c', 'd', 'b']invece di ['c', 'd'].
skbly7,

4
Il modo più veloce è la soluzione ovviamente più ingenua. Ho testato tutte le soluzioni proposte per la differenza simmetrica in questo thread e il vincitore è: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

1234

Esiste un modo migliore con ES7:


Intersezione

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

Differenza di intersezione Diagramma di Venn

Perché [1,2,3] [2,3]cederà [2,3]. D'altra parte, perché [1,2,3] [2,3,5]restituirà la stessa cosa.


Differenza

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

Differenza giusta Diagramma di Venn

Perché [1,2,3] [2,3]cederà [1]. D'altra parte, perché [1,2,3] [2,3,5]restituirà la stessa cosa.


Per una differenza simmetrica , puoi fare:

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

Differenza simmetrica Diagramma di Venn

In questo modo, otterrai una matrice contenente tutti gli elementi di arr1 che non sono in arr2 e viceversa

Come ha sottolineato @Joshaven Potter nella sua risposta, puoi aggiungerlo ad Array.prototype in modo che possa essere usato in questo modo:

Array.prototype.diff = function(arr2) { return this.filter(x => !arr2.includes(x)); }
[1, 2, 3].diff([2, 3])

3
Preferisco controllare < 0invece di== -1
Vic

1
Il calcolo della Arraydifferenza è un cosiddetto set operation, perché la ricerca di proprietà è il lavoro proprio di Sets, che sono ordini di grandezza più veloci di indexOf/ includes. In poche parole, la tua soluzione è molto inefficiente e piuttosto lenta.

@ftor ma con Set, i valori devono essere unici, no?
CervEd

1
@LuisSieira Ho capito che avrebbe funzionato [1,2,3] [2,3,5]dato che i numeri sono unici, ma se avessi detto [1,1,2,3] [1,2,3,5]e previsto [1]che non avresti potuto usare Set. La tua soluzione non funzionerebbe però: - / Ho finito per creare questa funzione perché non riuscivo a trovare un modo soddisfacente per farlo in modo più succinto. Se hai qualche idea su come farlo, mi piacerebbe saperlo!
CervEd,

3
Array.includes()ES7 non è la funzione invece di ES6? (1) (2) - e per continuare, con ES6 potresti usare Array.some()ad es let intersection = aArray.filter(a => bArray.some(b => a === b)). No?
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"]

Nota indexOf e il filtro non sono disponibili in ie prima di ie9.


50
L'unico browser che conta che non supporta il filtro e indexOf è IE8. IE9 li supporta entrambi. Quindi non è sbagliato.
Bryan Larsen,

14
ie7 e ie8 sono ancora (purtroppo) molto rilevanti, tuttavia è possibile trovare il codice polyfill per entrambe le funzioni sul sito MDN: developer.mozilla.org/en/JavaScript/Reference/Global_Objects/… developer.mozilla.org/en/JavaScript/ Riferimento / Global_Objects / ... Carica il codice elencato in "compatibilità" tramite un IE condizionale e BOOM. Ie7 / 8 sono supportati.
1nfiniti

44
Questa soluzione ha un tempo di esecuzione di O (n ^ 2) una soluzione lineare sarebbe molto più efficiente.
Jholloman,

75
Se si utilizza la funzione in questo modo: [1,2,3].diff([3,4,5])verrà restituita [1,2]invece di [1,2,4,5]non risolvere il problema nella domanda originale, qualcosa di cui tenere conto.
Bugster,

12
@AlinPurcaru Non supportato dai browser arcaici! = Sbagliato. Considerando Netscape 2.0, la maggior parte del codice JS qui è "errato" da questa definizione. È una cosa sciocca da dire.
NullUserException

304

Questo è di gran lunga il modo più semplice per ottenere esattamente il risultato che stai cercando, usando jQuery:

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

diffora contiene ciò che era in old_arrayquello che non è innew_array


4
@Batman Sì, ma solo se sono riferimenti allo stesso oggetto ( {a: 1} != {a: 1}) ( prova )
Matmarbon

È possibile utilizzarlo con array che contengono dati di un oggetto personalizzato? L'ho provato in realtà allo stesso modo ma non ha funzionato. Qualsiasi idea sarà altamente apprezzabile.
LetMeCodeYou

8
È un trucco? Il doc considera questo metodo come parte dei metodi dell'elemento DOM e non come un aiuto generale dell'array. Quindi potrebbe funzionare in questo modo ora, ma forse non nelle versioni future, in quanto non è stato progettato per usarlo in questo modo. Anche se, sarei felice se sarebbe ufficialmente un aiuto generale dell'array.
robsch,

1
@robsch Quando si utilizza .notcon un array, jQuery utilizza l'utilità integrata.grep() specifica per filtrare gli array. Non riesco a vederlo cambiare.
superfonico il

1
@vsync Sembra che tu stia cercando una differenza simmetrica
superfonica

158

Il metodo di differenza in Underscore (o nella sua sostituzione drop-in, Lo-Dash ) può fare anche questo:

(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]

Come con qualsiasi funzione Underscore, puoi anche usarlo in uno stile più orientato agli oggetti:

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

4
Penso che sia una buona soluzione dal punto di vista delle prestazioni, soprattutto perché lodash e underscore continuano a lottare per la migliore implementazione. Inoltre, è compatibile con IE6.
mahemoff,

4
Attenzione, questa implementazione non funzionerà per array di oggetti. Vedere stackoverflow.com/q/8672383/14731 per ulteriori informazioni.
Gili,

1
Come una delle risposte menzionate lì, funziona se è lo stesso oggetto, ma non se due oggetti hanno le stesse proprietà. Penso che vada bene dato che le nozioni di uguaglianza variano (ad es. Potrebbe anche essere un attributo "id" in alcune app). Tuttavia, sarebbe utile se si potesse passare un test di confronto a intersect ().
mahemoff

Per i posteri: Lodash ora ha _.differenceBy () che richiede un callback per fare il confronto; se si confrontano oggetti, è possibile inserire una funzione che li confronta di cui è necessario.
SomeCallMeTim

2
Fai attenzione se l'ordine degli argomenti si inverte, non funzionerà. per esempio. _.difference ([5, 2, 10], [1, 2, 3, 4, 5]); non riesco a ottenere il diff
Russj

79

JavaScript semplice

Esistono due possibili interpretazioni per "differenza". Ti lascio scegliere quale vuoi. Dì che hai:

var a1 = ['a', 'b'     ];
var a2 = [     'b', 'c'];
  1. Se vuoi ottenere ['a'], usa questa funzione:

    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. Se vuoi ottenere ['a', 'c'](tutti gli elementi contenuti in una a1 o a2, ma non in entrambe, la cosiddetta differenza simmetrica ), usa questa funzione:

    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

Se stai usando lodash, puoi usare _.difference(a1, a2)(caso 1 sopra) o _.xor(a1, a2)(caso 2).

Se si utilizza Underscore.js, è possibile utilizzare _.difference(a1, a2) funzione per il caso 1.

Set ES6, per array molto grandi

Il codice sopra funziona su tutti i browser. Tuttavia, per array di grandi dimensioni con oltre 10.000 articoli, diventa piuttosto lento, poiché presenta una complessità O (n²). Su molti browser moderni, possiamo sfruttare l' Setoggetto ES6 per accelerare le cose. Lodash utilizza automaticamente Setquando è disponibile. Se non stai usando lodash, usa la seguente implementazione, ispirata al post sul blog di 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));
}

Appunti

Il comportamento di tutti gli esempi può essere sorprendente o non ovvio se ti interessa di -0, +0, NaN o array sparsi. (Per la maggior parte degli usi, questo non ha importanza.)


Grazie. mi hai salvato la giornata. Ho dovuto confrontare un array da 300K e la tua soluzione "Set" ha funzionato perfettamente. Questa dovrebbe essere la risposta accettata.
justadev,

52

Per ottenere la differenza simmetrica è necessario confrontare le matrici in entrambi i modi (o in tutti i modi in caso di più matrici)

inserisci qui la descrizione dell'immagine


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

Esempio:

// 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"]

Differenza tra array di oggetti

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

Esempio:

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

51

Un approccio più pulito in ES6 è la seguente soluzione.

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

Differenza

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

Intersezione

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

Unione disgiuntiva (differenza simmetrica)

[ ...a2.filter(d => !a1.includes(d)),
  ...a1.filter(d => !a2.includes(d)) ]

Funziona solo in una direzione. Ora immagina che a1 = ['a', 'b', 'e']: e non verrà estratto.
circa il

sì, è così che funziona la differenza nella teoria degli insiemi. (a2 -a1) quello che stai cercando è (a2-a1) + (a1-a2)
ifelse.codes

1
@imrok Credo che questo sia quello che stai cercando [... a2.filter (d =>! a1.includes (d)), ... (a1.filter (d =>! a2.includes (d)) )]
ifelse.codes

2
Bellissima soluzione, grazie!
Thiago Alves,

40

In questo caso è possibile utilizzare un set . È ottimizzato per questo tipo di operazione (unione, intersezione, differenza).

Assicurati che si applichi al tuo caso, una volta che non consente duplicati.

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}

4
Sembra una bella biblioteca! Che peccato non poter scaricare solo la Setfunzione senza dover ottenere tutto il resto ...
Blixt,

@Blixt Credo che tu possa scaricare tutto e includere solo il file set.js
Samuel Carrijo,

Il set è implementato anche nella chiusura di Google. closing-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);
  });
}

Unire entrambi gli array, i valori univoci verranno visualizzati solo una volta, quindi indexOf () sarà uguale a lastIndexOf ().


3
Sono d'accordo che questo è il modo più semplice e pulito e bello che non richiede il prototipo toccante. "Se non riesci a spiegarlo a un bambino di sei anni, non lo capisci da solo." - Albert Einstein
lacostenycoder,

18

per sottrarre un array da un altro, usa semplicemente lo snippet di seguito:

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

Restituirà ['1,' 2 ',' 6 '] che sono elementi del primo array che non esistono nel secondo.

Pertanto, in base al tuo esempio di problema, la soluzione esatta è il seguente codice:

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

14

Con l'arrivo di ES6 con set e operatore splat (al momento di funzionare solo in Firefox, controllare la tabella di compatibilità ), è possibile scrivere una delle seguenti righe:

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

che si tradurrà in [ "c", "d" ].


Solo per curiosità come è che diverso che farlob.filter(x => !a.indexOf(x)))
Chovy

1
@chovy è diverso nella complessità temporale. La mia soluzione è che la O(n + m)tua soluzione è O(n * m)dove n e m sono lunghezze di matrici. Prendi lunghe liste e la mia soluzione funzionerà in pochi secondi, mentre la tua richiederà ore.
Salvador Dali,

Che ne dici di confrontare un attributo di un elenco di oggetti? È possibile usare questa soluzione?
Chovy

2
a.filter(x => !b1.has(x))è più semplice. E nota la specifica richiede solo la complessità di essere n * f(m) + mcon f(m)sublineare in media. È meglio di n * m, ma non necessariamente n + m.
Oriol,

2
@SalvadorDali var difference = [...new Set([...a].filter(x => !b1.has(x)))];Perché stai creando un array 'a' duplicato? Perché stai trasformando il risultato del filtro in un set e poi di nuovo in un array? Non è equivalente avar difference = a.filter(x => !b1.has(x));
Deepak Mittal il

13

Approccio funzionale con ES2015

Il calcolo differencetra due matrici è una delle Setoperazioni. Il termine indica già che è Setnecessario utilizzare il tipo nativo , al fine di aumentare la velocità di ricerca. Ad ogni modo, ci sono tre permutazioni quando si calcola la differenza tra due set:

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

Ecco una soluzione funzionale che riflette queste permutazioni.

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

Giusto difference :

differencerè banale. È solo differencelcon argomenti capovolti. Puoi scrivere una funzione per comodità:const differencer = flip(differencel) . È tutto!

Simmetrico difference:

Ora che abbiamo quello destro e sinistro, anche l'implementazione del simmetrico differencediventa banale:

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

Immagino che questo esempio sia un buon punto di partenza per avere un'idea del significato della programmazione funzionale:

Programmazione con blocchi predefiniti che possono essere collegati in molti modi diversi.


12

Una soluzione che indexOf()verrà utilizzata andrà bene per array di piccole dimensioni, ma man mano che crescono in lunghezza, le prestazioni degli algoritmi si avvicinano O(n^2). Ecco una soluzione che funzionerà meglio per array molto grandi usando oggetti come array associativi per memorizzare le voci dell'array come chiavi; elimina automaticamente anche le voci duplicate ma funziona solo con valori stringa (o valori che possono essere archiviati in modo sicuro come stringhe):

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']

Vuoi usare Object.hasOwnProperty () ogni volta che stai facendo un "for in" su un oggetto. Altrimenti si corre il rischio di scorrere in loop tutti i campi aggiunti al prototipo dell'oggetto predefinito. (O solo il genitore del tuo oggetto) Inoltre hai bisogno solo di due loop, uno per la creazione di una tabella hash e l'altro cerca quella tabella hash.
Jholloman,

1
@jholloman Non sono d' accordo con rispetto . Ora che possiamo controllare l'enumerabilità per qualsiasi proprietà, presumibilmente dovresti includere qualsiasi proprietà che ottieni durante l'enumerazione.
Phrogz,

1
@Phrogz Un buon punto se sei preoccupato solo per i browser moderni. Sfortunatamente devo tornare su IE7 al lavoro, quindi l'età della pietra è il mio treno di pensieri predefinito e non tendiamo ad usare gli spessori.
jholloman,

10

La risposta sopra di Joshaven Potter è ottima. Ma restituisce elementi nella matrice B che non sono nella matrice C, ma non viceversa. Ad esempio, se var a=[1,2,3,4,5,6].diff( [3,4,5,7]);quindi genererà: ==> [1,2,6], ma no [1,2,6,7] , che è la differenza effettiva tra i due. Puoi ancora usare il codice di Potter sopra, ma semplicemente ripetere il confronto anche una volta al contrario:

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

Questo dovrebbe produrre: [ 1, 2, 6, 7 ]


9

Un altro modo per risolvere il problema

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]

Inoltre, è possibile utilizzare la sintassi della funzione freccia:

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]

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]

Non dovresti mai estendere un oggetto nativo in questo modo. Se lo standard viene introdotto differencecome funzione in una versione futura e questa funzione ha una firma della funzione diversa dalla tua, si romperà il codice o le librerie esterne che utilizzano questa funzione.
t.niese,

7

Soluzione molto semplice con la funzione filtro di 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);


6

Cosa ne pensi di questo:

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

Quindi in questo modo puoi fare array1.diff(array2)per ottenere la loro differenza (Orribile complessità temporale per l'algoritmo - O (array1.length x array2.length) Credo)


L'uso dell'opzione filtro è un'ottima idea ... tuttavia, non è necessario creare un metodo contiene per l'array. Ho trasformato la tua idea in una fodera ... Grazie per l'ispirazione!
Joshaven Potter,

Non è necessario definire la funzione Includes (). JS include () fa la stessa cosa.
Da Man,

4

Utilizzando http://phrogz.net/JS/ArraySetMath.js è possibile:

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"]

4
function diffArray(arr1, arr2) {
  var newArr = arr1.concat(arr2);
  return newArr.filter(function(i){
    return newArr.indexOf(i) == newArr.lastIndexOf(i);
  });
}

questo funziona per me


3
  • Soluzione JavaScript pura (senza librerie)
  • Compatibile con browser meno recenti (non utilizza filter)
  • O (n ^ 2)
  • fnParametro di callback opzionale che consente di specificare come confrontare gli elementi dell'array

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(', ')
);


È possibile memorizzare nella cache i valori di lunghezza per aumentare la velocità. Volevo raccomandare di accedere agli elementi dell'array senza verificare la lunghezza, ma a quanto pare quel semplice controllo produce quasi 100 volte la velocità.
Slotos,

Nessun motivo per memorizzare nella cache i lengthvalori. È già di proprietà normale. jsperf.com/array-length-caching
vp_arth

3

Funziona così: fondamentalmente unisci i due array, cerca i duplicati e invia ciò che non è duplicato in un nuovo array che fa la differenza.

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


3

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

3

Complessità simmetrica e lineare . Richiede 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));
}


2

Solo pensando ... per il bene di una sfida ;-) questo funzionerebbe ... (per array di base di stringhe, numeri, ecc.) Nessun array nidificato

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"]

Nota che l'ordinamento probabilmente non sarà come notato sopra ... ma se lo desideri, chiama .sort () sull'array per ordinarlo.


2

Volevo una funzione simile che includesse un vecchio array e un nuovo array e mi dava un array di oggetti aggiunti e un array di oggetti rimossi, e volevo che fosse efficiente (quindi no. Contiene!).

Puoi giocare con la mia soluzione proposta qui: http://jsbin.com/osewu3/12 .

Qualcuno può vedere eventuali problemi / miglioramenti a tale algoritmo? Grazie!

Elenco dei codici:

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

2

Stavo cercando una risposta semplice che non implicasse l'uso di librerie diverse, e mi è venuta in mente la mia che non credo sia stata menzionata qui. Non so quanto sia efficiente o altro ma funziona;

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

Per il mio codice ho bisogno di estrarre anche i duplicati, ma suppongo che non sia sempre preferito.

Immagino che il principale svantaggio sia che sta potenzialmente confrontando molte opzioni che sono già state respinte.


2

soluzione littlebit per la migliore risposta

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

questo prenderà in considerazione l'attuale tipo di elemento. b / c quando creiamo un [a1 [i]] converte un valore in stringa dal suo valore originale, quindi abbiamo perso il valore reale.


Questo non riesce ancora per una matrice di oggetti. var a = [{a: 1}], b = [{b: 2}] arr_diff (a, b) == [].
EricP,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.