Czy istnieje sposób na zwrócenie różnicy między dwiema tablicami w JavaScript?
Na przykład:
var a1 = ['a', 'b'];
var a2 = ['a', 'b', 'c', 'd'];
// need ["c", "d"]
javascript
arrays
array-difference
John Adawan
źródło
źródło
O(a1.length x log(a2.length))
- czy ta wydajność jest możliwa w JavaScript?Odpowiedzi:
Zakładam, że porównujesz normalną tablicę. Jeśli nie, musisz zmienić pętlę for na pętlę for .. in .
Lepszym rozwiązaniem, jeśli nie zależy ci na kompatybilności wstecznej, jest użycie filtra. Ale nadal to rozwiązanie działa.
źródło
var a1 = ['a', 'b'];
ivar a2 = ['a', 'b', 'c', 'd', 'b'];
, zwróci błędną odpowiedź , to znaczy['c', 'd', 'b']
zamiast['c', 'd']
.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; }
Istnieje lepszy sposób korzystania z ES7:
Skrzyżowanie
Bo
[1,2,3] [2,3]
się podda[2,3]
. Z drugiej strony, bo[1,2,3] [2,3,5]
zwróci to samo.Różnica
Dla
[1,2,3] [2,3]
się podda[1]
. Z drugiej strony, bo[1,2,3] [2,3,5]
zwróci to samo.Aby uzyskać różnicę symetryczną , możesz:
W ten sposób otrzymasz tablicę zawierającą wszystkie elementy arr1, których nie ma w arr2 i odwrotnie
Jak zauważył @Joshaven Potter w swojej odpowiedzi, możesz dodać to do Array.prototype, aby można go było użyć w następujący sposób:
źródło
< 0
zamiast== -1
Array
różnicy jest tak zwaneset operation
, ponieważ wyszukiwanie właściwości jest zadaniem własnymSet
s, które są o rząd wielkości szybciej niżindexOf
/includes
. Mówiąc wprost, twoje rozwiązanie jest bardzo nieefektywne i raczej powolne.Set
, wartości muszą być unikalne, nie?[1,2,3] [2,3,5]
biorąc pod uwagę, że liczby są unikalne, ale gdybyś powiedział[1,1,2,3] [1,2,3,5]
i spodziewał się[1]
, że nie będziesz mógł użyćSet
. Twoje rozwiązanie też by nie działało: - / Skończyłem tworzyć tę funkcję, ponieważ nie mogłem znaleźć zadowalającego sposobu, aby zrobić to bardziej zwięźle. Jeśli masz jakieś pomysły, jak to zrobić, chciałbym wiedzieć!Array.includes()
funkcja ES7 nie jest dostępna zamiast ES6? (1) (2) - i aby kontynuować, używając ES6 możesz użyćArray.some()
np.let intersection = aArray.filter(a => bArray.some(b => a === b))
Nie?Pokaż fragment kodu
Uwaga indeks i filtr nie są dostępne np. Przed ie9.
źródło
[1,2,3].diff([3,4,5])
wróci[1,2]
zamiast,[1,2,4,5]
więc nie rozwiąże problemu w pierwotnym pytaniu, o czym należy pamiętać.Jest to zdecydowanie najłatwiejszy sposób na uzyskanie dokładnie tego, czego szukasz, za pomocą jQuery:
diff
teraz zawiera to, co było wold_array
tym, czego nie manew_array
źródło
{a: 1} != {a: 1}
) ( dowód ).not
z tablicą, jQuery używa jej wbudowanego narzędzia,.grep()
które jest specjalnie do filtrowania tablic. Nie widzę tego zmieniającego się.Metoda różnicy w Underscore (lub jej zastąpienie, Lo-Dash ) również może to zrobić:
Podobnie jak w przypadku każdej funkcji podkreślenia, możesz również używać jej w stylu bardziej obiektowym:
źródło
Zwykły JavaScript
Istnieją dwie możliwe interpretacje „różnicy”. Pozwolę ci wybrać, który chcesz. Powiedz, że masz:
Jeśli chcesz uzyskać
['a']
, użyj tej funkcji:Jeśli chcesz dostać
['a', 'c']
(wszystkie elementy zawarte w jednym z nicha1
lub wa2
obu - tak zwana różnica symetryczna ), użyj tej funkcji:Lodash / Underscore
Jeśli używasz lodash, możesz użyć
_.difference(a1, a2)
(przypadek 1 powyżej) lub_.xor(a1, a2)
(przypadek 2).Jeśli używasz Underscore.js, możesz użyć
_.difference(a1, a2)
funkcji dla przypadku 1.Zestaw ES6, do bardzo dużych tablic
Powyższy kod działa we wszystkich przeglądarkach. Jednak w przypadku dużych tablic składających się z ponad 10 000 elementów staje się dość wolny, ponieważ ma złożoność O (n²). W wielu nowoczesnych przeglądarkach możemy skorzystać z
Set
obiektu ES6, aby przyspieszyć działanie. Lodash automatycznie używa,Set
gdy jest dostępny. Jeśli nie korzystasz z lodash, skorzystaj z następującej implementacji, zainspirowanej postem na blogu Axela Rauschmayera :Notatki
Zachowanie wszystkich przykładów może być zaskakujące lub nieoczywiste, jeśli zależy Ci na -0, +0, NaN lub rzadkich tablicach. (W przypadku większości zastosowań nie ma to znaczenia).
źródło
Aby uzyskać różnicę symetryczną , musisz porównać tablice na dwa sposoby (lub na wszystkie sposoby w przypadku wielu tablic)
ES7 (ECMAScript 2016)
ES6 (ECMAScript 2015)
ES5 (ECMAScript 5.1)
Przykład:
Różnica między tablicami obiektów
Przykład:
źródło
Czystsze podejście w ES6 to następujące rozwiązanie.
Różnica
Skrzyżowanie
Unia dysjunkcyjna (różnica symetryczna)
źródło
a1 = ['a', 'b', 'e']
: e nie zostanie wyodrębnione.W takim przypadku możesz użyć zestawu . Jest zoptymalizowany do tego rodzaju operacji (połączenie, przecięcie, różnica).
Upewnij się, że dotyczy twojej sprawy, gdy nie zezwala na powielanie.
źródło
Set
funkcji bez konieczności dostać wszystko inne ...Scal obie tablice, unikalne wartości pojawią się tylko raz, więc indexOf () będzie taki sam jak lastIndexOf ().
źródło
aby odjąć jedną tablicę od drugiej, po prostu użyj poniższego fragmentu:
Zwróci [„1,” 2 ”,„ 6 ”], które są elementami pierwszej tablicy, które nie istnieją w drugiej.
Dlatego, zgodnie z twoim przykładem problemu, dokładnym rozwiązaniem jest następujący kod:
źródło
Wraz z pojawieniem się ES6 z zestawami i operatorem splat (w chwili, gdy działa tylko w Firefoksie, sprawdź tabelę zgodności ), możesz napisać jedną linijkę:
co spowoduje
[ "c", "d" ]
.źródło
b.filter(x => !a.indexOf(x)))
O(n + m)
twoje rozwiązanie,O(n * m)
gdzie n i m są długościami tablic. Weź długie listy, a moje rozwiązanie uruchomi się w kilka sekund, a twoje zajmie kilka godzin.a.filter(x => !b1.has(x))
jest prostsze. I pamiętać, spec wymaga tylko złożoność sięn * f(m) + m
zf(m)
sublinear średnio. To lepsze niżn * m
, ale niekoniecznien + m
.var difference = [...new Set([...a].filter(x => !b1.has(x)))];
Dlaczego tworzysz zduplikowaną tablicę „a”? Dlaczego zamieniasz wynik filtra w zestaw, a następnie z powrotem w tablicę? Czy to nie jest równoważne zvar difference = a.filter(x => !b1.has(x));
Funkcjonalne podejście z ES2015
Obliczanie
difference
między dwoma tablicami jest jedną zSet
operacji. Termin ten już wskazuje, żeSet
należy użyć rodzimego typu, aby zwiększyć szybkość wyszukiwania. Tak czy inaczej, podczas obliczania różnicy między dwoma zestawami istnieją trzy kombinacje:Oto funkcjonalne rozwiązanie, które odzwierciedla te permutacje.
Po lewej
difference
:Po prawej
difference
:differencer
jest banalny. To tylkodifferencel
z odwróconymi argumentami. Możesz napisać funkcję dla wygody:const differencer = flip(differencel)
. To wszystko!Symetryczny
difference
:Teraz, gdy mamy lewą i prawą, implementacja symetrycznej również
difference
staje się trywialna:Myślę, że ten przykład jest dobrym punktem wyjścia do uzyskania wrażenia, co oznacza programowanie funkcjonalne:
Programowanie z elementami składowymi, które można łączyć ze sobą na wiele różnych sposobów.
źródło
Zastosowanie rozwiązania
indexOf()
będzie odpowiednie dla małych tablic, ale wraz ze wzrostem długości zbliża się wydajność algorytmuO(n^2)
. Oto rozwiązanie, które będzie działać lepiej dla bardzo dużych tablic, wykorzystując obiekty jako tablice asocjacyjne do przechowywania wpisów tablicy jako kluczy; eliminuje również automatycznie zduplikowane wpisy, ale działa tylko z wartościami ciągów (lub wartościami, które można bezpiecznie przechowywać jako ciągi znaków):źródło
Powyższa odpowiedź Joshaven Potter jest świetna. Ale zwraca elementy z tablicy B, których nie ma w tablicy C, ale nie na odwrót. Na przykład, jeśli
var a=[1,2,3,4,5,6].diff( [3,4,5,7]);
następnie wyświetli: ==>[1,2,6]
, ale nie[1,2,6,7]
, co jest faktyczną różnicą między nimi. Nadal możesz użyć powyższego kodu Pottera, ale po prostu powtórz porównanie raz wstecz:To powinno wygenerować:
[ 1, 2, 6, 7 ]
źródło
Kolejny sposób rozwiązania problemu
Możesz także użyć składni funkcji strzałek:
źródło
źródło
difference
jako funkcję w przyszłej wersji, a ta funkcja będzie miała inną sygnaturę funkcji niż twoja, spowoduje to uszkodzenie kodu lub obcych bibliotek, które korzystają z tej funkcji.Bardzo proste rozwiązanie z funkcją filtrowania JavaScript:
źródło
Co powiesz na to:
Więc w ten sposób możesz zrobić,
array1.diff(array2)
aby uzyskać ich różnicę (choć straszna złożoność czasu dla algorytmu - O (wydaje mi się, że macierz1.length x macierz2.length)źródło
Za pomocą http://phrogz.net/JS/ArraySetMath.js możesz:
źródło
to działa dla mnie
źródło
filter
)fn
parametr wywołania zwrotnego, który pozwala określić sposób porównywania elementów tablicyźródło
length
wartości. To już zwykła właściwość. jsperf.com/array-length-cachingDziała to: po prostu scal dwie tablice, poszukaj duplikatów i wepchnij to, co nie jest duplikowane, do nowej tablicy, co jest różnicą.
źródło
// podejście es6
źródło
Złożoność symetryczna i liniowa . Wymaga ES6.
źródło
jeszcze jedna odpowiedź, ale wydaje się, że nikt nie wspominał o jsperf, w którym porównują kilka algorytmów i obsługę technologii: https://jsperf.com/array-difference-javascript wydaje się, że użycie filtra daje najlepsze wyniki. dzięki
źródło
Po prostu myślę ... ze względu na wyzwanie ;-) czy to zadziała ... (dla podstawowych tablic ciągów, liczb itp.) Brak tablic zagnieżdżonych
Zauważ, że sortowanie prawdopodobnie nie będzie takie, jak wspomniano powyżej ... ale w razie potrzeby wywołaj funkcję .sort () na tablicy, aby ją posortować.
źródło
Chciałem podobnej funkcji, która przyjęła starą tablicę i nową tablicę i dała mi tablicę dodanych elementów i tablicę usuniętych elementów, i chciałem, aby była wydajna (więc nie. Zawiera!).
Możesz zagrać z moim proponowanym rozwiązaniem tutaj: http://jsbin.com/osewu3/12 .
Czy ktoś może zobaczyć jakiekolwiek problemy / ulepszenia tego algorytmu? Dzięki!
Lista kodów:
źródło
Szukałem prostej odpowiedzi, która nie wymagałaby używania różnych bibliotek, i wymyśliłem swoją własną, o której nie sądzę, żeby tu była wspomniana. Nie wiem, jak wydajna jest, ani nic, ale działa;
W przypadku mojego kodu potrzebuję również duplikatów, ale myślę, że nie zawsze jest to preferowane.
Myślę, że główną wadą jest to, że potencjalnie porównuje wiele opcji, które zostały już odrzucone.
źródło
poprawka littlebit dla najlepszej odpowiedzi
weźmie to pod uwagę obecny typ elementu. b / c, gdy tworzymy [a1 [i]], konwertuje wartość na ciąg znaków z jej pierwotnej wartości, więc straciliśmy wartość rzeczywistą.
źródło