Jak sprawdzić, czy tablica zawiera wartość w JavaScript?

3994

Jaki jest najbardziej zwięzły i wydajny sposób sprawdzenia, czy tablica JavaScript zawiera wartość?

To jedyny znany mi sposób:

function contains(a, obj) {
    for (var i = 0; i < a.length; i++) {
        if (a[i] === obj) {
            return true;
        }
    }
    return false;
}

Czy istnieje lepszy i bardziej zwięzły sposób na osiągnięcie tego?

Jest to bardzo ściśle związane z pytaniem Przepełnienie stosu Najlepszy sposób na znalezienie elementu w tablicy JavaScript? który adresuje wyszukiwanie obiektów w tablicy za pomocą indexOf.

ćwiek
źródło
49
właśnie przetestowane: Twoja droga jest w rzeczywistości najszybsza dla wszystkich przeglądarek: jsperf.com/find-element-in-obj-vs-array/2 (oprócz wcześniejszego zapisania a. długości w zmiennej) przy użyciu indexOf (jak w $ .inArray) jest znacznie wolniejszy
Jörn Berkefeld
17
wielu odpowiedziało, że Array # indexOf jest tutaj najlepszym wyborem. Ale jeśli chcesz czegoś, co można poprawnie rzutować na Boolean, użyj tego: ~[1,2,3].indexOf(4)zwróci 0, które zostanie ocenione jako fałsz, podczas gdy ~[1,2,3].indexOf(3)zwróci -3, które oceni jako prawdę.
lordvlad
8
~nie jest tym, czego chcesz użyć do konwersji na wartość logiczną, do tego potrzebujesz !. Ale w tym przypadku chcesz sprawdzić równość z -1, więc funkcja może się skończyć, nie return [1,2,3].indexOf(3) === -1; ~jest binarna, odwróci każdy bit wartości osobno.
mcfedr
14
@ Iordvlad [1,2,3].indexOf(4)faktycznie zwróci -1 . Jak wskazał @mcfedr, ~jest operatorem bitowym NIE , patrz ES5 11.4.8. Chodzi o to, że ponieważ binarna reprezentacja -1składa się tylko z 1, dopełnieniem jest 0, co jest oceniane jako fałsz. Uzupełnienie dowolnej innej liczby będzie niezerowe, a zatem prawdziwe. Więc ~działa dobrze i jest często używany w połączeniu z indexOf.
mknecht
5
Tytuł wprowadza w błąd. Gdzie jest [[1,2],[3,4]].includes([3,4])?
mplungjan

Odpowiedzi:

4373

Nowoczesne przeglądarki mają to Array#includes, co robi dokładnie to i jest szeroko obsługiwane przez wszystkich oprócz IE:

console.log(['joe', 'jane', 'mary'].includes('jane')); //true

Możesz także użyć Array#indexOf, który jest mniej bezpośredni, ale nie wymaga wypełniania wielopełniaczy w przypadku przestarzałych przeglądarek.


Wiele platform oferuje również podobne metody:

Zauważ, że niektóre frameworki implementują to jako funkcję, podczas gdy inne dodają tę funkcję do prototypu tablicy.

codeape
źródło
42
MooTools ma również Array.contains, który zwraca wartość logiczną, co brzmi jak prawdziwe pytanie tutaj.
Ryan Florence,
22
prototyp ma również Array.includefunkcję zwracającą wartość logiczną
102008
46
Jeśli używasz dobrej przeglądarki, możesz po prostu użyćarray.indexOf(object) != -1
Sam Soffes
13
Ponadto, nie używaj samego indeksu jako warunku, ponieważ pierwszy element zwróci 0 i zostanie oceniony jako fałsz
plus
241
inArrayto okropna nazwa dla funkcji, która zwraca indeks elementu, a -1jeśli nie istnieje. Spodziewałbym się zwrotu wartości logicznej.
Tim
433

Aktualizacja z 2019 r .: Ta odpowiedź pochodzi z 2008 r. (11 lat!) I nie ma zastosowania w nowoczesnym użyciu JS. Obiecana poprawa wydajności została oparta na teście porównawczym przeprowadzonym w przeglądarkach tego czasu. Może to nie być istotne w kontekście współczesnego wykonywania JS. Jeśli potrzebujesz łatwego rozwiązania, poszukaj innych odpowiedzi. Jeśli potrzebujesz najlepszej wydajności, sprawdź swoje standardy w odpowiednich środowiskach wykonawczych.

Jak powiedzieli inni, iteracja przez tablicę jest prawdopodobnie najlepszym sposobem, ale udowodniono, że malejąca whilepętla jest najszybszym sposobem na iterację w JavaScript. Możesz więc przepisać kod w następujący sposób:

function contains(a, obj) {
    var i = a.length;
    while (i--) {
       if (a[i] === obj) {
           return true;
       }
    }
    return false;
}

Oczywiście możesz równie dobrze rozszerzyć prototyp Array:

Array.prototype.contains = function(obj) {
    var i = this.length;
    while (i--) {
        if (this[i] === obj) {
            return true;
        }
    }
    return false;
}

A teraz możesz po prostu użyć następujących opcji:

alert([1, 2, 3].contains(2)); // => true
alert([1, 2, 3].contains('2')); // => false
Damir Zekić
źródło
22
„Sprawdzony” to mocne słowo. Silniki JS stale się poprawiają, a czas wykonania mierzony 3 lata temu jest strasznie przestarzały.
orip
2
@Damir - Zgadzam się. Być może zmień przykład, aby używał indexOf, jeśli jest dostępny, tak aby osoby kopiujące i wklejające ten kod na ślepo uzyskały najlepszą możliwą wydajność.
orip
1
@cbmeeks tak, opieka jest zdecydowanie potrzebna. Prawdopodobnie był to przypadek zrobienia for (o in array)
czegoś,
1
Najlepszym sposobem na to jest sprawdzenie, czy [1, 2, 3] .indexOf (1)> -1
Devin G Rhode
206

indexOf może, ale jest to „rozszerzenie JavaScript do standardu ECMA-262; jako takie może nie być obecne w innych implementacjach tego standardu”.

Przykład:

[1, 2, 3].indexOf(1) => 0
["foo", "bar", "baz"].indexOf("bar") => 1
[1, 2, 3].indexOf(4) => -1

AFAICS Microsoft nie oferuje żadnej alternatywy dla tego, ale możesz dodać podobną funkcjonalność do tablic w Internet Explorerze (i innych przeglądarkach, które nie obsługują indexOf), jeśli chcesz, jak ujawnia szybkie wyszukiwanie Google (na przykład ta ).

Cic
źródło
w rzeczywistości istnieje przykład implementacji rozszerzenia indexOf dla przeglądarek, które go nie obsługują, na stronie developer.mozilla.org, z którą się łączysz.
Lloyd Cotten
w rzeczywistości, jeśli dodasz indexof do prototypu Array dla przeglądarek, które go nie obsługują (tj. IE7), będą również próbowały zapętlić tę funkcję podczas zapętlania elementów w tablicy. paskudny.
CpILL
czy można sprawdzić obiekt. nie sądzę, żeby to
działało
168

ECMAScript 7 wprowadza Array.prototype.includes.

Można go użyć w następujący sposób:

[1, 2, 3].includes(2); // true
[1, 2, 3].includes(4); // false

Akceptuje również opcjonalny drugi argument fromIndex:

[1, 2, 3].includes(3, 3); // false
[1, 2, 3].includes(3, -1); // true

W przeciwieństwie do tego indexOf, który korzysta ze ścisłego porównania równości , includesporównuje za pomocą algorytmu równości SameValueZero . Oznacza to, że możesz wykryć, czy tablica zawiera NaN:

[1, 2, NaN].includes(NaN); // true

Również w przeciwieństwie indexOf, includesnie pominąć brakujące indeksy:

new Array(5).includes(undefined); // true

Obecnie jest to wersja robocza, ale można ją wypełnić, aby działała na wszystkich przeglądarkach.

Oriol
źródło
3
Nieobsługiwany dla IE i Microsfot Edge (2015) ( developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… )
Adriano Resende
1
Istotna jest również tabela kompatybilności z ES7 (wygląda na to, że Chrome ją obsługuje)
styfle
czy można sprawdzić obiekt. nie sądzę, żeby to
działało
128

Najważniejsze odpowiedzi zakładają prymitywne typy, ale jeśli chcesz dowiedzieć się, czy tablica zawiera obiekt z jakąś cechą, Array.prototype.some () jest bardzo eleganckim rozwiązaniem:

const items = [ {a: '1'}, {a: '2'}, {a: '3'} ]

items.some(item => item.a === '3')  // returns true
items.some(item => item.a === '4')  // returns false

Zaletą jest to, że iteracja jest przerywana po znalezieniu elementu, dzięki czemu oszczędza się niepotrzebnych cykli iteracji.

Ponadto dobrze pasuje do ifinstrukcji, ponieważ zwraca wartość logiczną:

if (items.some(item => item.a === '3')) {
  // do something
}

* Jak wskazał jamess w komentarzu, w momencie udzielania odpowiedzi, wrzesień 2018 r., Array.prototype.some()Jest w pełni obsługiwany: tabela wsparcia caniuse.com

Michał
źródło
1
Na dzień dzisiejszy, wrzesień 2018 r Array.prototype.some () jest w pełni obsługiwana: caniuse.com stół wsparcie
Jamess
1
Praca w węźle> = 8.10 dla AWS Node.js Lambda, więc to świetnie. Bardzo czyste i proste rozwiązanie! 👍🏻
Jordan
1
@jamess To może być dobrze obsługiwane, ale pamiętaj, że Arrow functionsw tym przykładzie nie są tak dobrze obsługiwane. Aby uzyskać więcej informacji, zobacz tutaj: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Kamil Witkowski
Czy występuje zwarcie? A może iteruje całą tablicę, nawet jeśli znajdzie wartość?
Douglas Gaskell
@DouglasGaskell przerywa iterację raz znalezioną (wspomnianą w odpowiedzi)
Michael
112

Powiedzmy, że zdefiniowałeś tablicę tak:

const array = [1, 2, 3, 4]

Poniżej znajdują się trzy sposoby sprawdzenia, czy tam jest 3. Wszystkie z nich powrócić albo truealbo false.

Metoda Native Array (od ES2016) ( tabela kompatybilności )

array.includes(3) // true

Jako niestandardowa metoda macierzy (sprzed ES2016)

// Prefixing the method with '_' to avoid name clashes
Object.defineProperty(Array.prototype, '_includes', { value: function (v) { return this.indexOf(v) !== -1 }})
array._includes(3) // true

Prosta funkcja

const includes = (a, v) => a.indexOf(v) !== -1
includes(array, 3) // true
William Malo
źródło
Zwraca prawdę, jeśli „b” znajduje się w tablicy „a” ... Nie wiem, jak inaczej to wytłumaczyć ...
William Malo
4
Ta część nie rozumiem „!! ~”. I myślę, że to nie zadziała w IE8, ponieważ IE8 nie obsługuje indexOf () na obiekcie Array.
svlada
62
„~” to operator, który wprowadza na podłogę, odwraca i odejmuje 1 od liczby. indexOf zwraca -1, jeśli się nie powiedzie, więc „~” zamienia -1 w „0”. za pomocą "!!" zamienia liczby w boleany (!! 0 === false)
william malo
1
Fajnie, ale poważnie ze względu na prostotę nie tylko a.indexOf (b)> - 1, ponieważ „> -1” .length === „!! ~” .length
super
2
Nazwałbym brak wiedzy o skutkach operatorami logicznymi nieprofesjonalnymi. Ale zgadzam się co do wartości czytelnego kodu, z pewnością zawarłbym to w wyraźnie oznaczonej funkcji. I właśnie to robi większość głównych frameworków JS.
okdewit
79

Oto implementacja kompatybilna z JavaScript 1.6Array.indexOf :

if (!Array.indexOf) {
    Array.indexOf = [].indexOf ?
        function(arr, obj, from) {
            return arr.indexOf(obj, from);
        } :
        function(arr, obj, from) { // (for IE6)
            var l = arr.length,
                i = from ? parseInt((1 * from) + (from < 0 ? l : 0), 10) : 0;
            i = i < 0 ? 0 : i;
            for (; i < l; i++) {
                if (i in arr && arr[i] === obj) {
                    return i;
                }
            }
            return -1;
        };
}
Már Örlygsson
źródło
Wygląda świetnie, ale jest trochę zagubiony: * Czy testy na liniach 1 i 3 nie są równoważne? * Czy nie byłoby lepiej przetestować prototyp i w razie potrzeby dodać funkcję do Array.prototype?
Avi Flax,
10
Nie są one ekwiwalentne. [].indexOfjest skrótem od Array.prototype.indexOf. My, programiści w obronie przed paranoidami Javascript, unikają za wszelką cenę rozszerzania natywnych prototypów.
Már Örlygsson
1
Czy nie [].indexOftworzy nowej tablicy, a następnie uzyskuje do niej dostęp indexOf, Array.prototype.indexOfpo prostu uzyskuje bezpośredni dostęp do prototypu?
alex
3
@alex tak [].indexOf === Array.prototype.indexOf(wypróbuj w FireBug), ale odwrotnie [].indexOf !== Array.indexOf.
Már Örlygsson,
57

Posługiwać się:

function isInArray(array, search)
{
    return array.indexOf(search) >= 0;
}

// Usage
if(isInArray(my_array, "my_value"))
{
    //...
}
Matías Cánepa
źródło
25
x ? true : falsejest zwykle zbędny. To jest tutaj.
Ry-
@minitech Dlaczego uważasz, że jest zbędny?
Matías Cánepa
8
array.indexOf(search) >= 0jest już wartością logiczną. Właśnie return array.indexOf(search) >= 0.
Ry-
@minitech dobrze dzięki! Właściwie nie wiedziałem, że taką konstrukcję można zwrócić. TIL coś nowego.
Matías Cánepa
Dosłownie każdy konstrukt w javascript może zostać zwrócony
BT
49

Rozszerzanie Arrayobiektu JavaScript jest naprawdę złym pomysłem, ponieważ wprowadzasz nowe właściwości (własne metody) do for-inpętli, które mogą uszkodzić istniejące skrypty. Kilka lat temu autorzy biblioteki Prototype musieli przeprojektować swoją implementację biblioteki, aby usunąć właśnie tego rodzaju rzeczy.

Jeśli nie musisz się martwić o kompatybilność z innymi skryptami JavaScript działającymi na twojej stronie, skorzystaj z niej, w przeciwnym razie poleciłbym bardziej niezręczne, ale bezpieczniejsze rozwiązanie funkcji wolnostojącej.

Peter Mortensen
źródło
22
Nie zgadzam się. Z tego właśnie powodu nie należy używać pętli For-in dla tablic. Korzystanie z pętli początkowych spowoduje uszkodzenie podczas korzystania z jednej z popularnych bibliotek js
Tomas
Czy można to uznać za łatanie małp? lol Niektórzy ludzie to lubią.
cbmeeks,
33

Jednowarstwowy:

function contains(arr, x) {
    return arr.filter(function(elem) { return elem == x }).length > 0;
}
AlonL
źródło
8
array.filter(e=>e==x).length > 0jest równoważne, array.some(e=>e==x)ale somejest bardziej wydajne
Apolo
28

Używam następujących:

Array.prototype.contains = function (v) {
    return this.indexOf(v) > -1;
}

var a = [ 'foo', 'bar' ];

a.contains('foo'); // true
a.contains('fox'); // false
Eduardo Cuomo
źródło
24
function contains(a, obj) {
    return a.some(function(element){return element == obj;})
}

Array.prototype.some () został dodany do standardu ECMA-262 w 5. edycji

dansalmo
źródło
jeśli używasz es6 niż cam, skróć go jakocontains = (a, obj) => a.some((element) => element === obj))
diEcho
Nawet IE9 obsługuje Array.prototype.some () od ECMAScript 5 .
Suncat2000
19

Mam nadzieję, że szybsza dwukierunkowa indexOf/ lastIndexOfalternatywna

2015 r

Podczas gdy nowa metoda obejmuje jest bardzo przyjemna, wsparcie jest w zasadzie zerowe na razie.

Już dawno myślałem o sposobie zastąpienia powolnych funkcji indexOf / lastIndexOf.

Znaleźliśmy już skuteczny sposób, patrząc na najlepsze odpowiedzi. Z tych wybrałem containsfunkcję opublikowaną przez @Damir Zekic, która powinna być najszybsza. Ale stwierdza również, że poziomy odniesienia pochodzą z 2008 r., A więc są nieaktualne.

Ja też wolę whilesię for, ale nie konkretnego powodu skończyło pisanie funkcji z pętli for. Można to również zrobić za pomocą while --.

Byłem ciekawy, czy iteracja jest znacznie wolniejsza, jeśli sprawdzę obie strony tablicy podczas jej wykonywania. Najwyraźniej nie, a więc ta funkcja jest około dwa razy szybsza niż te najczęściej wybierane. Oczywiście jest także szybszy niż rodzimy. To w prawdziwym świecie, w którym nigdy nie wiadomo, czy szukana wartość znajduje się na początku, czy na końcu tablicy.

Kiedy wiesz, że właśnie wypchnąłeś tablicę z wartością, użycie lastIndexOf pozostaje prawdopodobnie najlepszym rozwiązaniem, ale jeśli musisz podróżować przez duże tablice, a wynik może być wszędzie, może to być solidne rozwiązanie, aby przyspieszyć.

Dwukierunkowy indexOf / lastIndexOf

function bidirectionalIndexOf(a, b, c, d, e){
  for(c=a.length,d=c*1; c--; ){
    if(a[c]==b) return c; //or this[c]===b
    if(a[e=d-1-c]==b) return e; //or a[e=d-1-c]===b
  }
  return -1
}

//Usage
bidirectionalIndexOf(array,'value');

Test wydajności

http://jsperf.com/bidirectionalindexof

Jako test stworzyłem tablicę zawierającą 100 000 wpisów.

Trzy zapytania: na początku, w środku i na końcu tablicy.

Mam nadzieję, że to również Cię zainteresuje i przetestujesz wydajność.

Uwaga: Jak widać nieznacznie zmodyfikowałem containsfunkcję, aby odzwierciedlić dane wyjściowe indexOf i lastIndexOf (tak w zasadzie trueza pomocą indexi za falsepomocą -1). To nie powinno tego zaszkodzić.

Wariant prototypowy tablicy

Object.defineProperty(Array.prototype,'bidirectionalIndexOf',{value:function(b,c,d,e){
  for(c=this.length,d=c*1; c--; ){
    if(this[c]==b) return c; //or this[c]===b
    if(this[e=d-1-c] == b) return e; //or this[e=d-1-c]===b
  }
  return -1
},writable:false, enumerable:false});

// Usage
array.bidirectionalIndexOf('value');

Funkcję można również łatwo zmodyfikować, aby zwracała wartość prawda lub fałsz, a nawet obiekt, ciąg znaków lub cokolwiek to jest.

A oto whilewariant:

function bidirectionalIndexOf(a, b, c, d){
  c=a.length; d=c-1;
  while(c--){
    if(b===a[c]) return c;
    if(b===a[d-c]) return d-c;
  }
  return c
}

// Usage
bidirectionalIndexOf(array,'value');

Jak to jest możliwe?

Myślę, że proste obliczenia, aby uzyskać indeks odbity w tablicy, są tak proste, że są dwa razy szybsze niż wykonanie iteracji pętli.

Oto złożony przykład wykonywania trzech kontroli na iterację, ale jest to możliwe tylko przy dłuższych obliczeniach, które powodują spowolnienie kodu.

http://jsperf.com/bidirectionalindexof/2

cocco
źródło
18

Wydajność

Dzisiaj 2020.01.07 Przeprowadzam testy na MacOs HighSierra 10.13.6 na Chrome v78.0.0, Safari v13.0.4 i Firefox v71.0.0 dla 15 wybranych rozwiązań. Wnioski

  • rozwiązania oparte na JSON, Seti zaskakująco find(K, N, O) są najwolniej wszystkich przeglądarek
  • es6 includes(F) jest szybki tylko na chromie
  • rozwiązania oparte na for(C, D) i indexOf(G, H) są dość szybkie we wszystkich przeglądarkach na małych i dużych tablicach, więc prawdopodobnie są najlepszym wyborem dla wydajnego rozwiązania
  • rozwiązania, w których indeks zmniejsza się podczas pętli (B), są prawdopodobnie wolniejsze, ponieważ działa sposób buforowania procesora .
  • Testuję również dużą tablicę, gdy szukany element znajduje się w pozycji 66% długości tablicy, a rozwiązania oparte na for(C, D, E) dają podobne wyniki (~ 630 operacji / s - ale E na safari i firefoxie wynosiło 10- 20% wolniej niż C i D)

Wyniki

wprowadź opis zdjęcia tutaj

Detale

Wykonuję 2 przypadki testowe: dla tablicy z 10 elementami i tablicy z 1 milionem elementów. W obu przypadkach umieszczamy szukany element w środku tablicy.

Tablica mała - 10 elementów

Testy możesz wykonać na swoim urządzeniu TUTAJ

wprowadź opis zdjęcia tutaj

Tablica duża - 1.000.000 elementów

Testy możesz wykonać na swoim urządzeniu TUTAJ

wprowadź opis zdjęcia tutaj

Kamil Kiełczewski
źródło
16

Jeśli używasz JavaScript 1.6 lub nowszy (Firefox 1.5 lub nowszy), możesz użyć Array.indexOf . W przeciwnym razie myślę, że skończysz z czymś podobnym do oryginalnego kodu.

Andru Luvisi
źródło
16
function inArray(elem,array)
{
    var len = array.length;
    for(var i = 0 ; i < len;i++)
    {
        if(array[i] == elem){return i;}
    }
    return -1;
} 

Zwraca indeks tablicy, jeśli został znaleziony, lub -1, jeśli nie został znaleziony

LmC
źródło
16

Używamy tego fragmentu kodu (działa z obiektami, tablicami, ciągami znaków):

/*
 * @function
 * @name Object.prototype.inArray
 * @description Extend Object prototype within inArray function
 *
 * @param {mix}    needle       - Search-able needle
 * @param {bool}   searchInKey  - Search needle in keys?
 *
 */
Object.defineProperty(Object.prototype, 'inArray',{
    value: function(needle, searchInKey){

        var object = this;

        if( Object.prototype.toString.call(needle) === '[object Object]' || 
            Object.prototype.toString.call(needle) === '[object Array]'){
            needle = JSON.stringify(needle);
        }

        return Object.keys(object).some(function(key){

            var value = object[key];

            if( Object.prototype.toString.call(value) === '[object Object]' || 
                Object.prototype.toString.call(value) === '[object Array]'){
                value = JSON.stringify(value);
            }

            if(searchInKey){
                if(value === needle || key === needle){
                return true;
                }
            }else{
                if(value === needle){
                    return true;
                }
            }
        });
    },
    writable: true,
    configurable: true,
    enumerable: false
});

Stosowanie:

var a = {one: "first", two: "second", foo: {three: "third"}};
a.inArray("first");          //true
a.inArray("foo");            //false
a.inArray("foo", true);      //true - search by keys
a.inArray({three: "third"}); //true

var b = ["one", "two", "three", "four", {foo: 'val'}];
b.inArray("one");         //true
b.inArray('foo');         //false
b.inArray({foo: 'val'})   //true
b.inArray("{foo: 'val'}") //false

var c = "String";
c.inArray("S");        //true
c.inArray("s");        //false
c.inArray("2", true);  //true
c.inArray("20", true); //false
dr.dimitru
źródło
15

Jeśli wielokrotnie sprawdzasz, czy istnieje obiekt w tablicy, powinieneś się przyjrzeć

  1. Utrzymywanie sortowania tablicy przez cały czas, wykonując sortowanie wstawiania w tablicy (umieść nowe obiekty we właściwym miejscu)
  2. Dokonaj aktualizacji obiektów jako operacji usuwania + posortowane wstawianie i
  3. Użyj wyszukiwania binarnego w swoim contains(a, obj).
Ztyx
źródło
2
Lub, jeśli to możliwe, przestań całkowicie korzystać z tablicy, a zamiast tego użyj obiektu jako słownika, jak sugerowali MattMcKnight i ninjagecko.
joeytwiddle
13

Rozwiązanie, które działa we wszystkich nowoczesnych przeglądarkach:

function contains(arr, obj) {
  const stringifiedObj = JSON.stringify(obj); // Cache our object to not call `JSON.stringify` on every iteration
  return arr.some(item => JSON.stringify(item) === stringifiedObj);
}

Stosowanie:

contains([{a: 1}, {a: 2}], {a: 1}); // true

Rozwiązanie IE6 +:

function contains(arr, obj) {
  var stringifiedObj = JSON.stringify(obj)
  return arr.some(function (item) {
    return JSON.stringify(item) === stringifiedObj;
  });
}

// .some polyfill, not needed for IE9+
if (!('some' in Array.prototype)) {
  Array.prototype.some = function (tester, that /*opt*/) {
    for (var i = 0, n = this.length; i < n; i++) {
      if (i in this && tester.call(that, this[i], i, this)) return true;
    } return false;
  };
}

Stosowanie:

contains([{a: 1}, {a: 2}], {a: 1}); // true

Dlaczego warto skorzystać JSON.stringify?

Array.indexOforaz Array.includes(podobnie jak większość odpowiedzi tutaj) porównuj tylko przez odniesienie, a nie według wartości.

[{a: 1}, {a: 2}].includes({a: 1});
// false, because {a: 1} is a new object

Premia

Niezoptymalizowany jednowarstwowy ES6:

[{a: 1}, {a: 2}].some(item => JSON.stringify(item) === JSON.stringify({a: 1));
// true

Uwaga: Porównywanie obiektów pod względem wartości będzie działać lepiej, jeśli klucze są w tej samej kolejności, więc dla bezpieczeństwa możesz najpierw posortować klucze za pomocą pakietu takiego jak ten: https://www.npmjs.com/package/sort-keys


Zaktualizowano containsfunkcję o optymalizację perf. Dzięki itinance za wskazanie tego.

Igor Barbashin
źródło
Ten konkretny fragment kodu może działać w IE6 (nie testowałem), ale IE nie obsługiwał ES5 aż do IE9.
Mark Reed,
Ze względu na wydajność należy unikać łączenia w łańcuchy. Przynajmniej powinieneś unikać JSON.stringify "obj" w każdej pętli, ponieważ jest ona droga i spowolni twoją aplikację. Dlatego powinieneś uchwycić go przed pętlą for w zmiennej temp
itinance 14.04.17
1
@itinance dobry punkt. Zaktualizowałem includesfunkcję o twoją sugestię. Uruchomiłem jsperf z moją funkcją. Jest około 5 razy wolniejszy niż lodysz. Choć lodash nie można porównać pod względem wartości i nie można znaleźć {a: 1}w [{a: 1}]. Nie wiem, czy robi to jakaś biblioteka. Ale jestem ciekawy, czy istnieje jakiś bardziej wydajny i niezbyt skomplikowany sposób.
Igor Barbashin
Późna uwaga: nie działa to, powiedzmy, contains([{ a: 1, b: 2 }], { b: 2, a: 1 })ponieważ obiekty strunowe zachowują kolejność właściwości.
Heretic Monkey
1
@HereticMonkey, true. Dlatego dodałem sort-keysnotatkę na dole
Igor Barbashin
12

Użyj jakiejś funkcji lodash .

Jest zwięzły, dokładny i ma doskonałą obsługę wielu platform.

Akceptowana odpowiedź nawet nie spełnia wymagań.

Wymagania: Poleć najbardziej zwięzły i skuteczny sposób sprawdzenia, czy tablica JavaScript zawiera obiekt.

Zaakceptowana odpowiedź:

$.inArray({'b': 2}, [{'a': 1}, {'b': 2}])
> -1

Moja rekomendacja:

_.some([{'a': 1}, {'b': 2}], {'b': 2})
> true

Uwagi:

$ .inArray działa dobrze w celu ustalenia, czy istnieje wartość skalarna w tablicy skalarów ...

$.inArray(2, [1,2])
> 1

... ale pytanie wyraźnie wymaga skutecznego sposobu ustalenia, czy obiekt jest zawarty w tablicy.

Aby obsłużyć zarówno skalary, jak i obiekty, możesz to zrobić:

(_.isObject(item)) ? _.some(ary, item) : (_.indexOf(ary, item) > -1)
l3x
źródło
10

ECMAScript 6 ma elegancką propozycję znalezienia.

Metoda find wykonuje funkcję wywołania zwrotnego raz dla każdego elementu obecnego w tablicy, dopóki nie znajdzie takiego, w którym wywołanie zwrotne zwraca prawdziwą wartość. Jeśli taki element zostanie znaleziony, find natychmiast zwraca wartość tego elementu. W przeciwnym razie znajdź zwroty niezdefiniowane. wywołanie zwrotne jest wywoływane tylko dla indeksów tablicy, którym przypisano wartości; nie jest wywoływany dla indeksów, które zostały usunięte lub którym nigdy nie przypisano wartości.

Oto dokumentacja MDN na ten temat.

Funkcja wyszukiwania działa w ten sposób.

function isPrime(element, index, array) {
    var start = 2;
    while (start <= Math.sqrt(element)) {
        if (element % start++ < 1) return false;
    }
    return (element > 1);
}

console.log( [4, 6, 8, 12].find(isPrime) ); // Undefined, not found
console.log( [4, 5, 8, 12].find(isPrime) ); // 5

Możesz użyć tego w ECMAScript 5 i poniżej, definiując funkcję .

if (!Array.prototype.find) {
  Object.defineProperty(Array.prototype, 'find', {
    enumerable: false,
    configurable: true,
    writable: true,
    value: function(predicate) {
      if (this == null) {
        throw new TypeError('Array.prototype.find called on null or undefined');
      }
      if (typeof predicate !== 'function') {
        throw new TypeError('predicate must be a function');
      }
      var list = Object(this);
      var length = list.length >>> 0;
      var thisArg = arguments[1];
      var value;

      for (var i = 0; i < length; i++) {
        if (i in list) {
          value = list[i];
          if (predicate.call(thisArg, value, i, list)) {
            return value;
          }
        }
      }
      return undefined;
    }
  });
}
Pradeep Mahdevu
źródło
9

Chociaż array.indexOf(x)!=-1jest to najbardziej zwięzły sposób na zrobienie tego (i jest obsługiwany przez przeglądarki inne niż Internet Explorer od ponad dekady ...), nie jest to O (1), ale raczej O (N), co jest okropne. Jeśli tablica się nie zmieni, możesz przekonwertować tablicę na tablicę mieszającą, a następnie wykonaj table[x]!==undefinedlub ===undefined:

Array.prototype.toTable = function() {
    var t = {};
    this.forEach(function(x){t[x]=true});
    return t;
}

Próbny:

var toRemove = [2,4].toTable();
[1,2,3,4,5].filter(function(x){return toRemove[x]===undefined})

(Niestety, chociaż możesz utworzyć Array.prototype.contains, aby „zamrozić” tablicę i przechowywać tablicę hashtable w this._cache w dwóch wierszach, dałoby to złe wyniki, jeśli zdecydujesz się edytować tablicę później. JavaScript nie ma wystarczających zaczepów, aby pozwala zachować ten stan, w przeciwieństwie do Pythona na przykład.)

ninjagecko
źródło
9

Można użyć zestawu, który ma metodę „has ()”:

function contains(arr, obj) {
      var proxy = new Set(arr);
      if (proxy.has(obj))
        return true;
      else
        return false;
    }

    var arr = ['Happy', 'New', 'Year'];
    console.log(contains(arr, 'Happy'));

rlib
źródło
5
Wydaje mi się, że return proxy.has(obj)jest o wiele czystszy niż dwie linijki z oświadczeniem if-else tutaj
Maciej Bukowski,
function contains(arr, obj) { return new Set(arr).has(obj); }
Gordon Bean
8

Posługiwać się:

var myArray = ['yellow', 'orange', 'red'] ;

alert(!!~myArray.indexOf('red')); //true

Próbny

Aby dokładnie wiedzieć, co tilde ~robić w tym momencie, zapoznaj się z tym pytaniem. Co robi tylda, gdy poprzedza wyrażenie? .

Mina Gabriel
źródło
5
Zostało to opublikowane już pół roku temu i nie trzeba go powtarzać.
Shadow Wizard is Ear For You
3
W rzeczywistości nie został opublikowany. Nie jako odpowiedź, ale jako komentarz do odpowiedzi, a nawet wtedy nie jest to jasne i zwięzłe. Dzięki za opublikowanie, Mina Gabriel.
T.CK
6

OK, możesz po prostu zoptymalizować swoje kod, aby uzyskać wynik!

Jest wiele sposobów na zrobienie tego, które są czystsze i lepsze, ale chciałem tylko uzyskać wzór i zastosować się do tego JSON.stringify, po prostu zrób coś takiego w twoim przypadku:

function contains(a, obj) {
    for (var i = 0; i < a.length; i++) {
        if (JSON.stringify(a[i]) === JSON.stringify(obj)) {
            return true;
        }
    }
    return false;
}
Alireza
źródło
Późna uwaga: nie działa to, powiedzmy, contains([{ a: 1, b: 2 }], { b: 2, a: 1 })ponieważ obiekty strunowe zachowują kolejność właściwości.
Heretic Monkey
5

W żadnym razie nie najlepszy, ale byłem kreatywny i uzupełniałem repertuar.

Nie używaj tego

Object.defineProperty(Array.prototype, 'exists', {
  value: function(element, index) {

    var index = index || 0

    return index === this.length ? -1 : this[index] === element ? index : this.exists(element, ++index)
  }
})


// Outputs 1
console.log(['one', 'two'].exists('two'));

// Outputs -1
console.log(['one', 'two'].exists('three'));

console.log(['one', 'two', 'three', 'four'].exists('four'));

sqram
źródło
Czego powinieneś użyć, jeśli nie to?
bryc
@bryc może zaakceptowane rozwiązanie lub inne rozwiązanie stąd. Jeśli nie zależy ci na wydajności, możesz tego użyć
sqram
5

Zaskoczony, że do tego pytania wciąż nie dodano najnowszej składni, dodając moje 2 centy.

Powiedzmy, że mamy tablicę Object arrObj i chcemy w niej wyszukać obj.

Array.prototype. indexOf -> (zwraca indeks lub -1 ) jest zwykle używany do znajdowania indeksu elementu w tablicy. Można to również wykorzystać do wyszukiwania obiektu, ale działa tylko wtedy, gdy przekazujesz odwołanie do tego samego obiektu.

let obj = { name: 'Sumer', age: 36 };
let arrObj = [obj, { name: 'Kishor', age: 46 }, { name: 'Rupen', age: 26 }];


console.log(arrObj.indexOf(obj));// 0
console.log(arrObj.indexOf({ name: 'Sumer', age: 36 })); //-1

console.log([1, 3, 5, 2].indexOf(2)); //3

Array.prototype. zawiera -> (zwraca wartość prawda lub fałsz )

console.log(arrObj.includes(obj));  //true
console.log(arrObj.includes({ name: 'Sumer', age: 36 })); //false

console.log([1, 3, 5, 2].includes(2)); //true

Array.prototype. find -> (przyjmuje callback, zwraca pierwszą wartość / obiekt, który zwraca true w CB).

console.log(arrObj.find(e => e.age > 40));  //{ name: 'Kishor', age: 46 }
console.log(arrObj.find(e => e.age > 40)); //{ name: 'Kishor', age: 46 }

console.log([1, 3, 5, 2].find(e => e > 2)); //3

Array.prototype. findIndex -> (przyjmuje callback, zwraca indeks pierwszej wartości / obiektu, który zwraca true w CB).

console.log(arrObj.findIndex(e => e.age > 40));  //1
console.log(arrObj.findIndex(e => e.age > 40)); //1

console.log([1, 3, 5, 2].findIndex(e => e > 2)); //1

Ponieważ find i findIndex przyjmuje wywołanie zwrotne, możemy pobrać dowolny obiekt (nawet jeśli nie mamy referencji) z tablicy, twórczo ustawiając prawdziwy warunek.

Sumer
źródło
5

Wykorzystuje się proste rozwiązanie tego wymagania find()

Jeśli masz szereg obiektów takich jak poniżej,

var users = [{id: "101", name: "Choose one..."},
{id: "102", name: "shilpa"},
{id: "103", name: "anita"},
{id: "104", name: "admin"},
{id: "105", name: "user"}];

Następnie możesz sprawdzić, czy obiekt z twoją wartością jest już obecny, czy nie

let data = users.find(object => object['id'] === '104');

jeśli dane są zerowe, to nie ma administratora, w przeciwnym razie zwróci istniejący obiekt jak poniżej.

{id: "104", name: "admin"}

Następnie możesz znaleźć indeks tego obiektu w tablicy i zastąpić go, używając poniższego kodu.

let indexToUpdate = users.indexOf(data);
let newObject = {id: "104", name: "customer"};
users[indexToUpdate] = newObject;//your new object
console.log(users);

otrzymasz wartość jak poniżej

[{id: "101", name: "Choose one..."},
{id: "102", name: "shilpa"},
{id: "103", name: "anita"},
{id: "104", name: "customer"},
{id: "105", name: "user"}];

mam nadzieję, że to pomoże każdemu.

siedmiodniowa żałoba
źródło
5

    function countArray(originalArray) {
     
    	var compressed = [];
    	// make a copy of the input array
    	var copyArray = originalArray.slice(0);
     
    	// first loop goes over every element
    	for (var i = 0; i < originalArray.length; i++) {
     
    		var count = 0;	
    		// loop over every element in the copy and see if it's the same
    		for (var w = 0; w < copyArray.length; w++) {
    			if (originalArray[i] == copyArray[w]) {
    				// increase amount of times duplicate is found
    				count++;
    				// sets item to undefined
    				delete copyArray[w];
    			}
    		}
     
    		if (count > 0) {
    			var a = new Object();
    			a.value = originalArray[i];
    			a.count = count;
    			compressed.push(a);
    		}
    	}
     
    	return compressed;
    };
    
    // It should go something like this:
    
    var testArray = new Array("dog", "dog", "cat", "buffalo", "wolf", "cat", "tiger", "cat");
    var newArray = countArray(testArray);
    console.log(newArray);

Sanjay Magar
źródło