Jaki jest najprostszy, pozbawiony biblioteki kod do implementacji skrzyżowań tablic w javascript? Chcę pisać
intersection([1,2,3], [2,3,4,5])
i dostać
[2, 3]
Jaki jest najprostszy, pozbawiony biblioteki kod do implementacji skrzyżowań tablic w javascript? Chcę pisać
intersection([1,2,3], [2,3,4,5])
i dostać
[2, 3]
break
doSimple js loops
wzrostów OPS / s do ~ 10MOdpowiedzi:
Użyj kombinacji
Array.prototype.filter
iArray.prototype.indexOf
:Lub, jak sugeruje vrugtehagel w komentarzach, możesz użyć najnowszego
Array.prototype.includes
dla jeszcze prostszego kodu:W przypadku starszych przeglądarek:
źródło
intersection([1,2,1,1,3], [1])
zwraca[1, 1, 1]
. Czy nie powinien wrócić[1]
?array2.indexOf(n) != -1
jednego można również pisaćarray2.includes(n)
dla jeszcze prostszego kodu.Niszczenie wydaje się najprostsze, zwłaszcza jeśli możemy założyć, że dane wejściowe są posortowane:
Nieniszczące musi być włosy bardziej skomplikowane, ponieważ musimy śledzić indeksy:
źródło
intersect_safe
:length
jest właściwością w tablicach, a nie metodą. Jest takie undelared zmiennai
wresult.push(a[i]);
. Wreszcie, to po prostu nie działa w ogólnym przypadku: dwa obiekty, w których żaden nie jest większy od drugiego według>
operatora, niekoniecznie są równe.intersect_safe( [ {} ], [ {} ] )
, na przykład da (po naprawieniu wcześniej wymienionych błędów) tablicę z jednym elementem, co jest wyraźnie błędne..slice(0)
do utworzenia klonu tablicyintersect_safe
zamiast śledzenia indeksów.Jeśli twoje środowisko obsługuje Zestaw ECMAScript 6 , to jeden prosty i podobno wydajny (patrz link do specyfikacji) sposób:
Krótszy, ale mniej czytelny (również bez tworzenia dodatkowego skrzyżowania
Set
):Unikanie nowy
Set
zb
każdej chwili:Należy pamiętać, że podczas korzystania z zestawów będzie można dostać tylko odrębne wartości, co
new Set[1,2,3,3].size
ma wartość3
.źródło
[...setA]
składnia? Jakiś specjalny rodzaj operacji javascript?x => new Set(b).has(x)
funkcja strzałki nie zmieniab
się w zestaw za każdym razem, gdy jest wykonywana? Prawdopodobnie powinieneś zapisać ten zestaw w zmiennej.Korzystanie z Underscore.js lub lodash.js
źródło
Mój wkład w kategoriach ES6. Zasadniczo znajduje przecięcie tablicy z nieokreśloną liczbą tablic podanych jako argumenty.
źródło
[[0,1,2,3,4,5,6,7,8,9],[0,2,4,6,8],[4,5,6,7],[4,6]]
a następnie stosuje się.reduce()
. Pierwsza[0,1,2,3,4,5,6,7,8,9].filter( e => [0,2,4,6,8].includes(e)
operacja jest wykonywana, a wynik staje się nowyp
ic
staje się[4,5,6,7]
w następnej turze i kontynuuje tak dalej, aż nie będzie już więcejc
.prototype
niepotrzebnie.Polecam powyższe zwięzłe rozwiązanie, które przewyższa inne wdrożenia przy dużych nakładach. Jeśli wydajność przy małych nakładach ma znaczenie, sprawdź poniższe alternatywy.
Alternatywy i porównanie wydajności:
Zobacz poniższy fragment alternatywnych implementacji i sprawdź https://jsperf.com/array-intersection-comparison w celu porównania wydajności.
Pokaż fragment kodu
Wyniki w przeglądarce Firefox 53:
Operacje / s na dużych tablicach (10 000 elementów):
Operacje / s na małych tablicach (100 elementów):
źródło
intersect([1,2,2,3], [2,3,4,5])
zwraca[2, 2, 3]
.a.filter(b.includes)
. Powinien działać znacznie szybciej (tak samo jak aktualizacja funkcji).A może po prostu użyć tablic asocjacyjnych?
edytować:
źródło
Object.prototype
.d[b[i]] = true;
zamiastd[b[j]] = true;
(i
niej
). Ale edycja wymaga 6 znaków.Coś w tym stylu, choć nie sprawdzone dobrze.
PS: Algorytm przeznaczony tylko dla liczb i normalnych ciągów, przecięcie dowolnych tablic obiektów może nie działać.
źródło
Wydajność implementacji @ atk dla sortowanych tablic prymitywów można poprawić, używając .pop zamiast .shift.
Stworzyłem test porównawczy za pomocą jsPerf: http://bit.ly/P9FrZK . Korzystanie z .pop jest około trzy razy szybsze.
źródło
a[aLast] > b[bLast]
sięa[aLast].localeCompare(b[bLast]) > 0
(i tak samo zelse if
poniżej), to będzie działać na łańcuchach..pop
wynosi O (1) i.shift()
wynosi O (n)Korzystanie z jQuery :
źródło
c = $(b).filter(a);
, ale nie polecam polegania na jQuery do tego rodzaju manipulacji tablicami, ponieważ dokumentacja wspomina tylko, że działa ona dla elementów.W przypadku tablic zawierających tylko ciągi lub liczby możesz coś zrobić z sortowaniem, tak jak w przypadku niektórych innych odpowiedzi. W przypadku ogólnego przypadku tablic dowolnych obiektów nie sądzę, że można tego uniknąć na dłuższą metę. Poniżej przedstawiono skrzyżowanie dowolnej liczby tablic podanych jako parametry do
arrayIntersection
:źródło
firstArr
czyfirstArray
też nie zaktualizowałem wszystkich referencji. Naprawiony.Używanie ES2015 i zestawów jest dość krótkie. Akceptuje wartości podobne do tablic jak ciąg znaków i usuwa duplikaty.
źródło
Możesz użyć
Set
odthisArg
dniaArray#filter
i przyjąćSet#has
jako oddzwonienie.źródło
Drobne ulepszenie do najmniejszego tutaj (rozwiązanie filter / indexOf ), a mianowicie utworzenie indeksu wartości w jednej z tablic przy użyciu obiektu JavaScript, zmniejszy go z O (N * M) do „prawdopodobnie” czasu liniowego. źródło1 źródło2
To nie jest najprostsze rozwiązanie (jest to więcej kodu niż filter + indexOf ), ani też nie jest ono najszybsze (prawdopodobnie wolniejsze o stały współczynnik niż intersect_safe () ), ale wydaje się całkiem niezłą równowagą. Jest to bardzo prosta strona, przy jednoczesnym zapewnieniu dobrej wydajności i nie wymaga wstępnie posortowanych danych wejściowych.
źródło
Inne indeksowane podejście, które może przetwarzać dowolną liczbę tablic jednocześnie:
Działa tylko w przypadku wartości, które można oceniać jako ciągi znaków, i powinieneś przekazać je jako tablicę:
... ale w przejrzysty sposób przyjmuje obiekty jako parametr lub dowolny z elementów do przecięcia (zawsze zwraca tablicę wspólnych wartości). Przykłady:
EDYTOWAĆ: Właśnie zauważyłem, że jest to w pewnym sensie nieco wadliwe.
To znaczy: kodowałem to, myśląc, że tablice wejściowe same w sobie nie mogą zawierać powtórzeń (jak podany przykład nie).
Ale jeśli tablice wejściowe zawierają powtórzenia, przyniosłoby to błędne wyniki. Przykład (przy użyciu poniższej implementacji):
Na szczęście łatwo to naprawić, dodając indeksowanie drugiego poziomu. To jest:
Zmiana:
przez:
...i:
przez:
Kompletny przykład:
źródło
var v =
dodaniu wierszaif (typeof v == 'function') continue;
pominie dodawanie funkcji do wyników. Dzięki!if (typeof v == 'function')
, możemy użyć jego stringfikacji (v.toString()
) jako klucza do indeksu. Ale musimy zrobić coś, aby zachować go w nienaruszonym stanie. najłatwiejszym sposobem jest po prostu przypisanie oryginalnej funkcji jako wartości zamiast zwykłej wartości logicznej boolean, ale w takim przypadku najnowszy deindeksaton powinien również zostać zmieniony, aby wykryć ten warunek i przywrócić właściwą wartość (funkcję).Możesz użyć (dla wszystkich przeglądarek oprócz IE):
lub dla IE:
źródło
źródło
Z pewnymi ograniczeniami dotyczącymi danych, możesz to zrobić w czasie liniowym !
Dla dodatnich liczb całkowitych : użyj tablicy odwzorowującej wartości na wartość logiczną „widziany / nie widział”.
Istnieje podobna technika dla obiektów : weź klucz zastępczy, ustaw go na „true” dla każdego elementu w tablicy1, a następnie poszukaj tego klucza w elementach tablicy2. Posprzątaj, kiedy skończysz.
Oczywiście musisz upewnić się, że klucz nie pojawił się wcześniej, w przeciwnym razie zniszczysz swoje dane ...
źródło
Przyczynię się do tego, co dla mnie działa najlepiej:
źródło
„indexOf” dla IE 9.0, chrome, firefox, opera,
źródło
źródło
Funkcjonalne podejście z ES2015
Podejście funkcjonalne musi rozważyć użycie tylko czystych funkcji bez skutków ubocznych, z których każda dotyczy tylko jednego zadania.
Ograniczenia te zwiększają możliwość łączenia i ponownego wykorzystywania zaangażowanych funkcji.
Należy pamiętać, że
Set
używany jest typ macierzysty , który ma korzystną wydajność wyszukiwania.Unikaj duplikatów
Oczywiście wielokrotnie powtarzające się przedmioty z pierwszego
Array
są zachowane, podczas gdy drugiArray
jest zduplikowany. To może być lub nie być pożądane zachowanie. Jeśli potrzebujesz unikalnego wyniku, zastosujdedupe
pierwszy argument:Oblicz przecięcie dowolnej liczby
Array
sJeśli chcesz, aby obliczyć przecięcie numer dowolnie ustawionym na
Array
s tylko komponowaćintersect
zfoldl
. Oto funkcja wygody:źródło
(expr ? true : false)
jest zbędny. Używaj tylkoexpr
wtedy, gdy rzeczywiste booleany nie są potrzebne, tylko prawda / fałsz.Dla prostoty:
Korzyści:
Wady:
Nie chciałbyś używać tego do pracy z silnikiem 3D lub jądrem, ale jeśli masz problemy z uruchomieniem go w aplikacji opartej na zdarzeniach, twój projekt ma większe problemy.
źródło
.reduce
zbudować mapę i.filter
znaleźć skrzyżowanie.delete
w obrębie.filter
pozwala nam traktować drugą tablicę tak, jakby była unikalnym zestawem.Uważam to podejście za dość łatwe do uzasadnienia. Działa w stałym czasie.
źródło
Jest to prawdopodobnie najprostszy, poza list1.filter (n => list2.includes (n))
źródło
Jeśli potrzebujesz, aby obsługiwał przecinanie wielu tablic:
źródło
Prosty sposób w stylu ES6.
Przykład:
źródło
Napisałem funkcję przecięcia, która może nawet wykryć przecięcie tablicy obiektów na podstawie konkretnej właściwości tych obiektów.
Na przykład,
i chcemy przecięcia na podstawie
id
właściwości, wówczas dane wyjściowe powinny być:Jako taka funkcja tego samego (uwaga: kod ES6) to:
i możesz wywołać funkcję jako:
Uwaga: ta funkcja znajduje przecięcie, biorąc pod uwagę, że pierwsza tablica jest tablicą podstawową, a zatem wynikiem przecięcia będzie tablica podstawowa.
źródło
Myślę, że będzie to szybsze z czasem O (tablica 1 + tablica 2) przy założeniu, że map.has () to ~ O (1). Proszę, popraw mnie, jeśli się mylę.
źródło
Oto implementacja underscore.js :
Źródło: http://underscorejs.org/docs/underscore.html#section-62
źródło