Utilizzo di lodash per confrontare array (esistenza di oggetti senza ordine)


124

So di poterlo fare usando i loop, ma sto cercando di trovare un modo elegante per farlo:

Ho due array:

var array1 = [['a', 'b'], ['b', 'c']];
var array2 = [['b', 'c'], ['a', 'b']];

Voglio usare lodashper confermare che i due array precedenti sono gli stessi. Con "lo stesso" intendo che non vi è alcun elemento array1che non sia contenuto array2.

In termini di controllo dell'uguaglianza tra questi elementi:

['a', 'b'] == ['b', 'a'] 

o

['a', 'b'] == ['a', 'b'] 

Entrambi funzionano poiché le lettere saranno sempre in ordine.

Risposte:


224

Se si ordina l'array esterno, è possibile utilizzarlo _.isEqual()poiché l'array interno è già ordinato.

var array1 = [['a', 'b'], ['b', 'c']];
var array2 = [['b', 'c'], ['a', 'b']];
_.isEqual(array1.sort(), array2.sort()); //true

Nota che .sort()muterà gli array. Se questo è un problema per te, crea prima una copia utilizzando (ad esempio) .slice()o l'operatore di diffusione ( ...).

Oppure, fai come Daniel Budick consiglia in un commento qui sotto:

_.isEqual(_.sortBy(array1), _.sortBy(array2))

Lodash sortBy()non muterà l'array.


6
Tieni presente che array.sort () sta modificando l'array originale. Forse questo potrebbe essere migliore: var array1 = [['a', 'b'], ['b', 'c']]; var array2 = [['b', 'c'], ['a', 'b']]; _.isEqual ([... array1] .sort (), [... array2] .sort ()); // vero
Yaniv Efraim

3
Aggiunte due frasi che notano che .sort()muta e suggeriscono opzioni per la copia prima se questo è un problema per l'utente.
Trott

6
Se stai già usando lodash, potresti semplicemente _.isEqual(_.sortBy(array1), _sortBy(array2))prevenire la mutazione.
Daniel Budick

1
@DanielBudick Thanks! L'ho aggiunto alla risposta. Ottimo suggerimento.
Trott

32

Puoi usare i lodash xorper questo

doArraysContainSameElements = _.xor(arr1, arr2).length === 0

Se consideri l'array [1, 1] diverso dall'array [1], potresti migliorare le prestazioni in questo modo:

doArraysContainSameElements = arr1.length === arr2.length === 0 && _.xor(arr1, arr2).length === 0

1
Gli array devono comunque essere ordinati.
Sovattha Sok

1
Questo dovrebbe essere il modo migliore per la versione più recente
Leonardo

@Sovattha Sok Gli array non hanno bisogno di essere ordinati. Fare _.xor([3,4], [4,3]).length == 0ti renderà vero.
Nikolay

1
Un avvertimento: questa tecnica funziona bene per gli array "piccoli" ma può essere una prestazione dolorosa e dal punto di vista della memoria se gli array sono enormi e sono per lo più diversi perché _.xor () continuerà ad andare molto oltre la prima differenza. In altre parole non ritorna velocemente alla prima differenza rilevata.
XDS

6

Con "lo stesso" intendo che non ci sono elementi in array1 che non siano contenuti in array2.

Potresti usare flatten () e difference () per questo, che funziona bene se non ti interessa se ci sono elementi array2che non sono presenti array1. Sembra che tu stia chiedendo se array1 sia un sottoinsieme di array2 ?

var array1 = [['a', 'b'], ['b', 'c']];
var array2 = [['b', 'c'], ['a', 'b']];

function isSubset(source, target) {
    return !_.difference(_.flatten(source), _.flatten(target)).length;
}

isSubset(array1, array2); // → true
array1.push('d');
isSubset(array1, array2); // → false
isSubset(array2, array1); // → true

4

Ci sono già risposte qui, ma ecco la mia implementazione JS pura. Non sono sicuro che sia ottimale, ma sicuramente è trasparente, leggibile e semplice.

// Does array a contain elements of array b?
const contains = (a, b) => new Set([...a, ...b]).size === a.length
const isEqualSet = (a, b) => contains(a, b) && contains(b, a)

La logica contains()è che se acontiene tutti gli elementi di b, metterli nello stesso set non cambierebbe le dimensioni.

Ad esempio, if const a = [1,2,3,4]e const b = [1,2], then new Set([...a, ...b]) === {1,2,3,4}. Come puoi vedere, il set risultante ha gli stessi elementi dia .

Da lì, per renderlo più conciso, possiamo ridurlo a quanto segue:

const isEqualSet = (a, b) => {
  const unionSize = new Set([...a, ...b])
  return unionSize === a.length && unionSize === b.length
}

1

PURE JS (funziona anche quando array e sottoarray hanno più di 2 elementi con ordine arbitrario). Se le stringhe contengono, ,usa come join('-')carattere parametr (può essere utf) che non è usato nelle stringhe

array1.map(x=>x.sort()).sort().join() === array2.map(x=>x.sort()).sort().join()


0

Possiamo usare la _.differencefunzione per vedere se c'è qualche differenza o no.

function isSame(arrayOne, arrayTwo) {
   var a = _.unique(arrayOne),
       b = _.unique(arrayTwo);

   if (a.length <= b.length) {
      a = arrayTwo;
      b = arrayOne;
      return _.isEmpty(_.difference(a.sort(), b.sort()));
   } else {
      return false;
   }

}

// examples
console.log(isSame([1, 2, 3], [1, 2, 3])); // true
console.log(isSame([1, 2, 4], [1, 2, 3])); // false
console.log(isSame([1, 2], [2, 3, 1])); // false
console.log(isSame([2, 3, 1], [1, 2])); // false

// Test cases pointed by Mariano Desanze, Thanks.
console.log(isSame([1, 2, 3], [1, 2, 2])); // false
console.log(isSame([1, 2, 2], [1, 2, 2])); // true
console.log(isSame([1, 2, 2], [1, 2, 3])); // false

Spero che questo ti possa aiutare.


5
Sbagliato, la tua funzione otterrà trueperconsole.log(isSame([1,2], [2,3,1]));
David Lin

3
Grazie @DavidLin per segnalarlo. Ho apportato le modifiche per considerare quel caso. Grazie e ci scusiamo per il disagio.
Amitesh

2
non è necessario cambiare posizione per a & b se le lunghezze non sono le stesse. Se le lunghezze sono diverse, non possono essere già le stesse, quindi se (arrayOne.lenght! == arrayTwo.lenght) restituisce false;
Alex

1
-1 Non ha senso avere quelle ae le bvariabili. È solo utilizzare tali variabili all'interno della if-thenparte, e la prima cosa che fai c'è scartare quei valori caricati alla linea 2. penso la seguente riga singola funzionerà esattamente la stessa cosa: return arrayOne.length <= arrayTwo.length && _.isEmpty(_.difference(arrayTwo.sort(), arrayTwo.sort());. E <=può anche essere migliorato in ===.
Mariano Desanze

1
E _.differencerestituirà gli elementi mancanti del 1 ° argomento, ma non gli elementi mancanti nel 2 ° argomento. Quindi questo sarà in modo non corretto tornare truequando si ripete articoli 1 ° al 2 °: isSame([1, 2, 3], [1, 2, 2]).
Mariano Desanze

0

Modifica: ho perso l'aspetto multidimensionale di questa domanda, quindi lo lascio qui nel caso in cui aiuti le persone a confrontare array unidimensionali

È una vecchia domanda, ma avevo problemi con la velocità di utilizzo di .sort()o sortBy(), quindi ho usato questo invece:

function arraysContainSameStrings(array1: string[], array2: string[]): boolean {
  return (
    array1.length === array2.length &&
    array1.every((str) => array2.includes(str)) &&
    array2.every((str) => array1.includes(str))
  )
}

Doveva fallire velocemente e per i miei scopi funziona bene.


L'ultimo controllo è davvero necessario? array1.every((str) => array2.includes(str))dovrebbe essere sufficiente. Anche l'OP voleva usare lodash, dovresti almeno dire perché proponi una soluzione vanillaJS (... ora che abbiamo tutto e include ...). Fornisci anche un esempio di come applicare la tua funzione al problema fornito (array bidimensionali).
line-o

Questo è un buon punto: non ho affrontato l'aspetto multidimensionale, non il requisito del lodash. Ho pensato che sarebbe stato utile che chiunque cercasse (come ho fatto io) per lodash methods to compare arrays without considering ordervedere un'alternativa nel moderno Javascript. Il secondo controllo è necessario in quanto arraysContainSameStrings(['1', '2', '2'], ['1', '2', '3'])altrimenti restituirebbe true. Lo lascio qui, perché potrebbe aiutare, ma apprezzo di non aver risposto alla domanda
charliematters
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.