Używanie lodash do porównywania tablic (istnienie elementów bez kolejności)

125

Wiem, że mogę to zrobić za pomocą pętli, ale staram się znaleźć elegancki sposób na zrobienie tego:

Mam dwie tablice:

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

Chcę użyć, lodashaby potwierdzić, że powyższe dwie tablice są takie same. Przez „ten sam” mam na myśli, że nie ma w nim elementu, array1który nie jest zawarty w array2.

Jeśli chodzi o sprawdzanie równości między tymi elementami:

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

lub

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

Oba działają, ponieważ litery zawsze będą w porządku.

pQuestions123
źródło

Odpowiedzi:

224

Jeśli sortujesz zewnętrzną tablicę, możesz użyć, _.isEqual()ponieważ wewnętrzna tablica jest już posortowana.

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

Zauważ, że .sort()spowoduje to mutację tablic. Jeśli to dla ciebie problem, najpierw wykonaj kopię za pomocą (na przykład) .slice()lub operatora rozłożenia ( ...).

Lub postępuj zgodnie z zaleceniami Daniela Budicka w komentarzu poniżej:

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

Lodash sortBy()nie zmieni tablicy.

Trott
źródło
6
Weź pod uwagę, że array.sort () modyfikuje oryginalną tablicę. Może ten byłby lepszy: var tablica1 = [['a', 'b'], ['b', 'c']]; var tablica2 = [['b', 'c'], ['a', 'b']]; _.isEqual ([... tablica1] .sort (), [... tablica2] .sort ()); // prawda
Yaniv Efraim
3
Dodano dwa zdania, zauważając, że .sort()mutuje i sugerując opcje kopiowania w pierwszej kolejności, jeśli stanowi to problem dla użytkownika.
Trott
6
Jeśli już używasz lodash, możesz po prostu zrobić, _.isEqual(_.sortBy(array1), _sortBy(array2))aby zapobiec mutacji.
Daniel Budick,
1
@DanielBudick Thanks! Dodałem to do odpowiedzi. Świetna sugestia.
Trott
33

Możesz xordo tego użyć lodash

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

Jeśli uważasz, że tablica [1, 1] różni się od tablicy [1], możesz nieco poprawić wydajność w następujący sposób:

doArraysContainSameElements = arr1.length === arr2.length === 0 && _.xor(arr1, arr2).length === 0
Stephan Hoyer
źródło
1
Tablice i tak trzeba posortować.
Sovattha Sok
1
To powinien być lepszy sposób dla nowszej wersji
Leonardo
@Sovattha Sok Tablice nie muszą być sortowane. Dzięki _.xor([3,4], [4,3]).length == 0temu staniesz się prawdą.
Nikolay
1
Słowo ostrzeżenia: ta technika działa dobrze w przypadku „małych” tablic, ale może być uciążliwa i związana z pamięcią, jeśli tablice są ogromne i są w większości różne, ponieważ _.xor () będzie nadal przekraczać pierwszą różnicę. Innymi słowy, nie powraca szybko przy pierwszej wykrytej różnicy.
XDS
6

Przez „ten sam” rozumiem, że nie ma pozycji w tablicy1, która nie jest zawarta w tablicy2.

Można użyć Spłaszczenie () i różnicę () dla tego, który działa dobrze, jeśli nie obchodzi, czy istnieją pozycje array2, które nie są w array1. Wygląda na to, że pytasz, czy tablica1 jest podzbiorem tablicy2 ?

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
Adam Boduch
źródło
4

Tutaj są już odpowiedzi, ale oto moja czysta implementacja JS. Nie jestem pewien, czy jest optymalny, ale na pewno jest przejrzysty, czytelny i prosty.

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

Powodem contains()jest to, że jeśli azawiera wszystkie elementy programu b, to umieszczenie ich w tym samym zestawie nie zmieniłoby rozmiaru.

Na przykład, jeśli const a = [1,2,3,4]i const b = [1,2]wtedy new Set([...a, ...b]) === {1,2,3,4}. Jak widać, wynikowy zestaw zawiera te same elementy, co a.

Stamtąd, aby uczynić go bardziej zwięzłym, możemy sprowadzić to do następujących:

const isEqualSet = (a, b) => {
  const unionSize = new Set([...a, ...b])
  return unionSize === a.length && unionSize === b.length
}
J.Ko
źródło
1

PURE JS (działa również, gdy tablice i podtablice mają więcej niż 2 elementy o dowolnej kolejności). Jeśli łańcuchy zawierają znak ,użyj jako join('-')parametru (może to być utf), który nie jest używany w łańcuchach

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

Kamil Kiełczewski
źródło
0

Możemy użyć _.differencefunkcji, aby zobaczyć, czy jest jakaś różnica, czy nie.

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

Mam nadzieję, że to Ci pomoże.

Amitesh
źródło
5
Źle, twoja funkcja będzie truezaconsole.log(isSame([1,2], [2,3,1]));
David Lin
3
Dziękuję @DavidLin za wskazanie tego. Dokonałem zmian, aby rozważyć ten przypadek. Dziękuję i przepraszam za niedogodności.
Amitesh
2
nie musisz zamieniać miejscami a i b, jeśli długości nie są takie same. Jeśli długości się różnią, nie mogą już być takie same, więc jeśli (arrayOne.lenght! == arrayTwo.lenght) return false;
Alex,
1
-1 Nie ma sensu mieć tych ai bzmiennych. Ty tylko korzystać z tych zmiennych wewnątrz if-thenstrony, a pierwszą rzeczą, jaką możesz zrobić tam jest odrzucić te wartości załadowane na linii 2. Myślę, że po jednej linii będzie działać dokładnie tak samo: return arrayOne.length <= arrayTwo.length && _.isEmpty(_.difference(arrayTwo.sort(), arrayTwo.sort());. A <=można również poprawić się ===.
Mariano Desanze
1
I _.differencezwróci brakujące elementy pierwszego argumentu, ale nie brakuje elementów w drugim. Więc to będzie nieprawidłowo powrócić truekiedy powtórzyć przedmioty na 1 w 2: isSame([1, 2, 3], [1, 2, 2]).
Mariano Desanze
0

Edycja: przegapiłem wielowymiarowy aspekt tego pytania, więc zostawiam to tutaj na wypadek, gdyby pomogło to ludziom porównać jednowymiarowe tablice

To stare pytanie, ale miałem problemy z szybkością używania .sort()lub sortBy(), więc zamiast tego użyłem tego:

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

Miał on szybko zawieść i do moich celów działa dobrze.

charliematters
źródło
Czy ostatnia kontrola jest naprawdę konieczna? array1.every((str) => array2.includes(str))powinno wystarczyć. Również OP chciał użyć lodash, powinieneś przynajmniej powiedzieć, dlaczego proponujesz rozwiązanie vanillaJS (... teraz, gdy mamy wszystko i zawiera ...). Proszę również podać przykład, jak zastosować swoją funkcję do podanego problemu (tablice dwuwymiarowe).
line-o
To dobra uwaga - nie odniosłem się do tego wielowymiarowego aspektu, nie do wymagania dla lodash. Pomyślałem, że przydałoby się, aby ktokolwiek szukał (tak jak ja), lodash methods to compare arrays without considering orderaby zobaczyć alternatywę we współczesnym JavaScript. Drugie sprawdzenie jest konieczne, ponieważ w arraysContainSameStrings(['1', '2', '2'], ['1', '2', '3'])przeciwnym razie zwróciłoby true. Zostawię to tutaj, ponieważ może to pomóc, ale doceniam, że nie odpowiedziałem na pytanie
charliematters