Metoda indexOf w tablicy obiektów?

512

Jaka jest najlepsza metoda uzyskania indeksu tablicy zawierającej obiekty?

Wyobraź sobie ten scenariusz:

var hello = {
    hello: 'world',
    foo: 'bar'
};
var qaz = {
    hello: 'stevie',
    foo: 'baz'
}

var myArray = [];
myArray.push(hello,qaz);

Chciałbym teraz, aby indexOfobiekt miał hellowłaściwość, 'stevie'która w tym przykładzie byłaby 1.

Jestem całkiem nowym użytkownikiem JavaScript i nie wiem, czy istnieje prosta metoda, czy też powinienem zbudować własną funkcję, aby to zrobić.

Antonio Laguna
źródło
1
Czy chcesz scalić dwa obiekty helloi qaz?
Armin,
Nie, nie wiem. Chcę mieć listę obiektów w tablicy.
Antonio Laguna,
Ah, dobrze! Chcesz poznać pozycję całego obiektu w tablicy, która ma zdefiniowaną właściwość.
Armin,
10
Znalazłem bardzo prostą funkcję, aby rozwiązać dokładnie ten problem z tą odpowiedzią SO: var elementPos = array.map(function(x) {return x.id; }).indexOf(idYourAreLookingFor); var objectFound = array[elementPos]; [link] ( stackoverflow.com/a/16100446/1937255 )
Rick
ES6 Array.indexOf jest lepsza niż zaakceptowana odpowiedź (jeśli ES6 działa dla Ciebie) - patrz pełny przykład poniżej
1

Odpowiedzi:

1042

Myślę, że możesz rozwiązać go w jednym wierszu za pomocą funkcji mapy :

pos = myArray.map(function(e) { return e.hello; }).indexOf('stevie');
Pablo Francisco Pérez Hidalgo
źródło
58
To powinna być szczerze przyjęta odpowiedź. Większość przeglądarek obsługuje obecnieArray.prototype.map()
AlbertEngelB
10
Nie jest obsługiwany przez IE8, ale jeśli nie jest to problem, jest to najlepsze rozwiązanie.
Antonio Laguna
57
Um ... czy nie warto zauważyć, że Array.prototype.map () tworzy zupełnie nową tablicę zawierającą zmapowane elementy? Więc jeśli masz tablicę z 1000 elementami, najpierw stworzyłeś inną tablicę z 1000 elementami, a następnie ją przeszukasz? Warto byłoby zobaczyć wydajność tej metody w porównaniu do prostej pętli for. Zwłaszcza gdy korzystasz z platformy mobilnej z ograniczonymi zasobami.
Doug
7
@Doug Chociaż twoje zdanie na temat wydajności jest rzeczywiście słuszne, kto przy zdrowych zmysłach zastąpiłby jeden wiersz kodu siódmym dla aplikacji, która jest prawie z definicji związana IO / Network, dopóki nie profilują się na wąskie gardła?
Jared Smith
6
Technicznie zminimalizowany plik js jest również jedną linią; D
większyKing
371

Array.prototype.findIndex jest obsługiwany we wszystkich przeglądarkach innych niż IE (non-edge). Ale podany polifill jest fajny.

var indexOfStevie = myArray.findIndex(i => i.hello === "stevie");

Rozwiązanie z mapą jest w porządku. Ale iterujesz po całej tablicy przy każdym wyszukiwaniu. To tylko najgorszy przypadek dla findIndex, który przestaje iterować po znalezieniu dopasowania.


Nie ma tak naprawdę zwięzłego sposobu (gdy deweloperzy musieli martwić się o IE8) , ale oto wspólne rozwiązanie:

var searchTerm = "stevie",
    index = -1;
for(var i = 0, len = myArray.length; i < len; i++) {
    if (myArray[i].hello === searchTerm) {
        index = i;
        break;
    }
}

lub jako funkcja:

function arrayObjectIndexOf(myArray, searchTerm, property) {
    for(var i = 0, len = myArray.length; i < len; i++) {
        if (myArray[i][property] === searchTerm) return i;
    }
    return -1;
}
arrayObjectIndexOf(arr, "stevie", "hello"); // 1

Tylko kilka notatek:

  1. Nie używaj do ... w pętlach na tablicach
  2. Pamiętaj, aby wyjść z pętli lub powrócić z funkcji po znalezieniu „igły”
  3. Uważaj na równość obiektów

Na przykład,

var a = {obj: 0};
var b = [a];
b.indexOf({obj: 0}); // -1 not found
Joe
źródło
funkcja ma niepoprawne porównanie wyszukiwarki, ponieważ powinno być searchTerm :)
Antonio Laguna,
było wiele razy
Joe
3
@ SteveBennett to wersja zoptymalizowana pod kątem wydajności; długość tablicy musi być określona tylko raz (gdy inicjowane są zmienne dla pętli for). W twoim przypadku długość jest sprawdzana na nowo przy każdej iteracji. Zobacz także stackoverflow.com/questions/5349425/… i stackoverflow.com/questions/8452317/... Jeśli jednak wydajność nie jest wysoka, nie ma to większego znaczenia.
loother 20.04.16
1
Dobra odpowiedź, ale przeprowadziłem testy porównawcze wydajności (patrz jsperf.com/find-index-of-object-in-array-by-contents ) i stwierdziłem, że wspomniana tutaj odpowiedź oparta na funkcji wydaje się drugą najbardziej wydajną odpowiedzią. Jedyną wydajniejszą rzeczą jest umieszczenie go w prototypie zamiast w funkcji, jak wspomniano w mojej odpowiedzi .
Uniphonic
123

W ES2015 jest to dość łatwe:

myArray.map(x => x.hello).indexOf('stevie')

lub prawdopodobnie z lepszą wydajnością dla większych tablic:

myArray.findIndex(x => x.hello === 'stevie')
Steve Bennett
źródło
2
Dobre podejście do używania ES6
kag
Byłem zaskoczony, że żadna z tych metod nie jest tak wydajna jak prototypowana pętla, jak wspomniano w mojej odpowiedzi. Mimo że obsługa przeglądarki metody findIndex jest nieco słaba, wygląda na to, że robiłaby to samo, ale nadal jest mniej wydajna? Zobacz link w mojej odpowiedzi dla testów porównawczych.
Uniphonic
Dobrze wiedzieć, czy wydajność jest ważna dla danego zadania. Z mojego doświadczenia to bardzo rzadko, ale ymmv.
Steve Bennett
24
var idx = myArray.reduce( function( cur, val, index ){

    if( val.hello === "stevie" && cur === -1 ) {
        return index;
    }
    return cur;

}, -1 );
Esailija
źródło
17

Podoba mi się odpowiedź Pablo, ale Array # indexOf i Array # map nie działają we wszystkich przeglądarkach. Podkreślenie użyje kodu natywnego, jeśli jest dostępny, ale ma również awarie. Dodatkowo ma metodę wyrywkową do robienia dokładnie tego, co robi anonimowa metoda mapy Pablo.

var idx = _.chain(myArray).pluck("hello").indexOf("Stevie").value();
tandrewnichole
źródło
1
Array.prototype.map () jest obsługiwany dla IE9 + i można użyć funkcji Polyfill dla IE8, 7, 6: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Johann Echavarria
1
Możesz użyć polifillu. . . lub możesz po prostu użyć podkreślenia lub lodash, które są w zasadzie polifillami, do których dołączono całą masę innych gadżetów . Jaki jest sprzeciw wobec podkreślenia? Rozmiar?
tandrewnichols
Bardzo podoba mi się podkreślenie, twoja odpowiedź jest również sprytna, ale odpowiedź IMHO Pablo jest najczystsza.
Johann Echavarria
Wow, nigdy nie myślałem, żeby użyć takiego łańcucha. Bardzo podoba mi się to, jak sprawia, że ​​wyszukiwanie przebiega płynnie.
Dylan Pierce
chainjest tutaj zbędny. _.pluck(myArray, 'hello').indexOf('stevie')
Steve Bennett,
14

Lub prototypuj:

Array.prototype.indexOfObject = function arrayObjectIndexOf(property, value) {
    for (var i = 0, len = this.length; i < len; i++) {
        if (this[i][property] === value) return i;
    }
    return -1;
}

myArr.indexOfObject("name", "stevie");
Nathan Zaetta
źródło
9
Bardzo wygodne! Chociaż wybrałbym prototype.indexOfObject, aby nie zakłócać istniejącej metody Array.indexOf. Array.prototype.indexOfObject = function(property, value) { for (var i = 0, len = this.length; i < len; i++) { if (this[i][property] === value) return i; } return -1; };
Adam
1
Owinęłbym to w samowykonczalne zamknięcie ze starym zapisem wcześniej, przy czym pierwsza linia funkcji zastępującej jest czymś podobnym do if (typeof property === 'string' || typeof property === 'number' || typeof property === 'boolean') return oldIndexOf(property, value);. Jest tak, ponieważ jest to kilka rodzajów, które są niezmienne. Chciałbym również podać trzeci argument, aby w razie potrzeby umożliwić powrót do metody natywnej.
Isiah Meadows
8

Krótki

myArray.indexOf('stevie','hello')

Przypadków użycia :

  /*****NORMAL****/  
[2,4,5].indexOf(4) ;//OUTPUT 1
 /****COMPLEX*****/
 [{slm:2},{slm:4},{slm:5}].indexOf(4,'slm');//OUTPUT 1
 //OR
 [{slm:2},{slm:4},{slm:5}].indexOf(4,function(e,i){
   return e.slm;
});//OUTPUT 1
/***MORE Complex**/
[{slm:{salat:2}},{slm:{salat:4}},{slm:{salat:5}}].indexOf(4,function(e,i){
   return e.slm.salat;
});//OUTPUT 1

API:

    Array.prototype.indexOfOld=Array.prototype.indexOf

    Array.prototype.indexOf=function(e,fn){
      if(!fn){return this.indexOfOld(e)}
      else{ 
       if(typeof fn ==='string'){var att=fn;fn=function(e){return e[att];}}
        return this.map(fn).indexOfOld(e);
      }
    };
Abdennour TOUMI
źródło
6

Zrobiłem tutaj testy wydajności różnych odpowiedzi, które każdy może samodzielnie uruchomić:

https://jsperf.com/find-index-of-object-in-array-by-contents

Na podstawie moich pierwszych testów w Chrome następująca metoda (użycie pętli for skonfigurowanej w prototypie) jest najszybsza:

Array.prototype.indexOfObject = function (property, value) {
    for (var i = 0, len = this.length; i < len; i++) {
        if (this[i][property] === value) return i;
    }
    return -1;
}

myArray.indexOfObject("hello", "stevie");

Ten kod jest nieco zmodyfikowaną wersją odpowiedzi Nathana Zaetty.

W testach wydajności wypróbowałem go zarówno z celem znajdującym się pośrodku (indeks 500), jak i na samym końcu (indeks 999) tablicy obiektów 1000, a nawet jeśli wstawię cel jako ostatni element tablicy (co oznacza że musi przechodzić przez każdy element w tablicy, zanim zostanie znaleziony), wciąż kończy się najszybciej.

Zaletą tego rozwiązania jest to, że jest jednym z najbardziej zwięzłych do wielokrotnego wykonywania, ponieważ należy powtórzyć tylko ostatnią linię:

myArray.indexOfObject("hello", "stevie");
Unifoniczny
źródło
2
Właśnie miałem opublikować odpowiedź na to pytanie, używając skrzypiec z własnymi testami, ale dzięki twojej odpowiedzi już nie muszę. Chcę tylko potwierdzić twoje testy - osiągnąłem ten sam wynik, ale użyłem whilepętli zamiast forjednej i performance.now(). Chciałbym, aby ta odpowiedź była jeszcze bardziej doceniona i że widziałem ją wcześniej, zaoszczędziłoby mi to trochę czasu ...
Yin Cognyto,
5

Porównałem kilka metod i uzyskałem wynik w najszybszy sposób na rozwiązanie tego problemu. To jest forpętla. Jest ponad 5 razy szybszy niż jakakolwiek inna metoda.

Oto strona testu: https://jsbench.me/9hjewv6a98

John Klimov
źródło
Brakuje for-ofpętli nie?
Douglas Gaskell
5

Chociaż większość innych odpowiedzi tutaj jest poprawnych. Czasami najlepiej jest po prostu zrobić krótką, prostą funkcję w pobliżu miejsca, gdzie będziesz jej używać.

// indexOf wrapper for the list of objects
function indexOfbyKey(obj_list, key, value) {
    for (index in obj_list) {
        if (obj_list[index][key] === value) return index;
    }
    return -1;
}
// Find the string in the list (default -1)
var test1 = indexOfbyKey(object_list, 'name', 'Stevie');
var test2 = indexOfbyKey(object_list, 'last_name', 'some other name');

To zależy od tego, co jest dla Ciebie ważne. Może oszczędzać wiersze kodu i bardzo sprytnie jest korzystać z jednowierszowego lub umieszczać ogólne rozwiązanie gdzieś, które obejmuje różne przypadki brzegowe. Ale czasem lepiej jest po prostu powiedzieć: „tutaj zrobiłem to w ten sposób”, niż pozostawić przyszłym programistom dodatkowe prace inżynierii odwrotnej. Zwłaszcza jeśli uważasz się za „nowicjusza” jak w swoim pytaniu.

SpiRail
źródło
4
array.filter(function(item, indx, arr){ return(item.hello === 'stevie'); })[0];

Uważaj na [0].

Należy używać reducejak wAntonio Laguna odpowiedzi.

Przepraszamy za zwięzłość ...

Cody
źródło
4

Jeśli twój obiekt jest tym samym obiektem, którego używasz w tablicy, powinieneś być w stanie uzyskać indeks obiektu w taki sam sposób, jak robisz to tak, jakby to był ciąg znaków.

var hello = {
    hello: 'world',
    foo: 'bar'
};
var qaz = {
    hello: 'stevie',
    foo: 'baz'
}

var qazCLONE = { // new object instance and same structure
    hello: 'stevie',
    foo: 'baz'
}

var myArray = [hello,qaz];

myArray.indexOf(qaz) // should return 1
myArray.indexOf(qazCLONE) // should return -1
Caio Koiti
źródło
Oto odpowiedź, której szukałem, ponieważ nie było dla mnie jasne, czy IndexOf pasuje do wartości czy co. Teraz wiem, że mogę użyć IndexOf do znalezienia mojego obiektu i nie martw się, jeśli istnieją inne obiekty o tych samych właściwościach.
MDave
3

prosty:

myArray.indexOf(myArray.filter(function(item) {
    return item.hello == "stevie"
})[0])
sęp płowy
źródło
3

Jeśli jesteś zainteresowany tylko znalezieniem stanowiska, zobacz odpowiedź @ Pablo .

pos = myArray.map(function(e) { return e.hello; }).indexOf('stevie');

Jednakże, jeśli nie możesz się doczekać znalezienia elementu (tj. Jeśli zastanawiasz się nad zrobieniem czegoś takiego myArray[pos]), istnieje bardziej efektywny, jednowierszowy sposób, używając tego filter.

element = myArray.filter((e) => e.hello === 'stevie')[0];

Zobacz wyniki wydajności (~ + 42% operacji / s): http://jsbench.github.io/#7fa01f89a5dc5cc3bee79abfde80cdb3

zurfyx
źródło
2

Zobacz ten przykład: http://jsfiddle.net/89C54/

for (i = 0; i < myArray.length; i++) {
    if (myArray[i].hello === 'stevie') {
        alert('position: ' + i);
        return;
    }
}

Zaczyna się od zera.

Armin
źródło
2

Zrobiłem ogólną funkcję, aby sprawdzić, czy poniżej jest kod i działa dla każdego obiektu

function indexOfExt(list, item) {
    var len = list.length;

    for (var i = 0; i < len; i++) {
        var keys = Object.keys(list[i]);
        var flg = true;
        for (var j = 0; j < keys.length; j++) {
            var value = list[i][keys[j]];
            if (item[keys[j]] !== value) {
                flg = false;
            }
        }
        if (flg == true) {
            return i;
        }
    }
    return -1;
}

var items = [{ "hello": 'world', "foo": 'bar' }];
var selectedItem = { "hello": 'world', "foo": 'bar' };
alert(items.indexOf(selectedItem));
alert(indexOfExt(items, selectedItem));

Pierwszy alert zwróci -1 (oznacza, że ​​nie znaleziono dopasowania), a drugi alert zwróci 0 (oznacza, że ​​znaleziono dopasowanie).

Shiljo Paulson
źródło
2

Użyj _.findIndexz biblioteki underscore.js

Oto przykład _.findIndex([{a:1},{a: 2,c:10},{a: 3}], {a:2,c:10}) //1

niren
źródło
Jeśli sugerujesz metody z dodatkowych bibliotek, powinieneś wspomnieć, skąd one pochodzą.
Craicerjack
@ Steve Bennett niezłe wykorzystanie biblioteki teraz jest w lodash
zabusa
2

Używając findIndexmetody ES6 , bez lodash ani żadnych innych bibliotek, możesz napisać:

function deepIndexOf(arr, obj) {
  return arr.findIndex(function (cur) {
    return Object.keys(obj).every(function (key) {
      return obj[key] === cur[key];
    });
  });
}

Spowoduje to porównanie bezpośrednich właściwości obiektu, ale nie powtórzy się we właściwościach.

Jeśli twoja implementacja findIndexjeszcze nie zapewnia (większość tego nie robi), możesz dodać lekki wypełniacz, który obsługuje to wyszukiwanie:

function deepIndexOf(arr, obj) {
  function findIndex = Array.prototype.findIndex || function (pred) {
    for (let i = 0; i < this.length; ++i) {
      if (pred.call(this, this[i], i)) {
        return i;
      }
    }

    return -1;
  }

  return findIndex.call(arr, function (cur) {
    return Object.keys(obj).every(function (key) {
      return obj[key] === cur[key];
    });
  });
}

(z mojej odpowiedzi na ten duplikat )

ssube
źródło
2

Array.prototype.findIndex()Zasadniczo możesz korzystać z natywnej i wygodnej funkcji :

Metoda findIndex () zwraca indeks w tablicy, jeśli element w tablicy spełnia podaną funkcję testową. W przeciwnym razie zwracane jest -1.

Uwaga: nie jest obsługiwana w przeglądarkach Internet Explorer, Opera i Safari, ale możesz skorzystać z funkcji Polyfill podanej w poniższym linku.

Więcej informacji:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/findIndex

var hello = {
  hello: 'world',
  foo: 'bar'
};
var qaz = {
  hello: 'stevie',
  foo: 'baz'
}

var myArray = [];
myArray.push(hello, qaz);

var index = myArray.findIndex(function(element, index, array) {
  if (element.hello === 'stevie') {
    return true;
  }
});
alert('stevie is at index: ' + index);

GibboK
źródło
2

Oprócz odpowiedzi @Monika Garg możesz użyć findIndex()(Istnieje wiele wypełniaczy dla niepoprawnych przeglądarek).

Widziałem, że ludzie głosowali za tą odpowiedzią i mam nadzieję, że zrobili to z powodu złej składni, ponieważ moim zdaniem jest to najbardziej elegancki sposób.

Metoda findIndex () zwraca indeks w tablicy, jeśli element w tablicy spełnia podaną funkcję testową. W przeciwnym razie zwracane jest -1.

Na przykład:

var hello = {
  hello: 'world',
  foo: 'bar'
};
var qaz = {
  hello: 'stevie',
  foo: 'baz'
}

var myArray = [];
myArray.push(hello,qaz);

var index = myArray.findIndex(function(element) {
  return element.hello == 'stevie';
});

alert(index);

Mosh Feu
źródło
Metoda wydaje się elegancka, ale nie obsługuje IE zgodnie z MDN? developer.mozilla.org/en/docs/Web/JavaScript/Reference/…
Jaakko Karhu
Spróbuj użyć wypełnienia (link w odpowiedzi).
Mosh Feu
1

W ten sposób można znaleźć indeks obiektu w tablicy

    var myArray = [{  hello: 'world',
        foo: 'bar'
    },{
        hello: 'stevie',
        foo: 'baz'
    }];



    for (i = 0; i < myArray.length; i++) {
        if (myArray[i].hello === 'stevie') {
            alert('position: ' + i);
            return;
        }
    }
Asad Fida
źródło
0

Działa to bez niestandardowego kodu

var arr, a, found;
arr = [{x: 1, y: 2}];
a = {x: 1, y: 2};
found = JSON.stringify(arr).indexOf(JSON.stringify(a)) > - 1;
// found === true

Uwaga: to nie daje rzeczywistego indeksu, informuje tylko, czy Twój obiekt istnieje w bieżącej strukturze danych

Xeltor
źródło
1
To nie jest poprawne, ponieważ nie pozwala na uzyskanie żadnego indeksu
Antonio Laguna
0

Możesz po prostu użyć

const someId = 2;
const array = [{id:1}, {id:2}, {id:3}];
const index = array.reduce((i, item, index) => item.id === someId ? index : i, -1);
alert('someId ' + someId + ' is at index ' + index);

Bez podkreślenia, nie dla, po prostu redukcja.

7ynk3r
źródło
0
var hello = {hello: "world",  foo: "bar"};
var qaz = {hello: "stevie", foo: "baz"};
var myArray = [];
myArray.push(hello,qaz);

function indexOfObject( arr, key, value   ) {
    var j = -1;
    var result = arr.some(function(obj, i) { 
        j++;
        return obj[key] == value;
    })

    if (!result) {
        return -1;
    } else {
        return j;
    };
}

alert(indexOfObject(myArray,"hello","world"));
użytkownik3235365
źródło
Użyj metody Array.
user3235365,
-1

Aby to zrobić, możesz stworzyć własny prototyp:

coś jak:

Array.prototype.indexOfObject = function (object) {
    for (var i = 0; i < this.length; i++) {
        if (JSON.stringify(this[i]) === JSON.stringify(object))
            return i;
    }
}
Janx z Wenezueli
źródło
2
Zła praktyka, przełamywanie enkapsulacji: developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/…
HMR
Spowodowałoby to również uszkodzenie rekurencyjnie zdefiniowanych obiektów.
Joseph Coco
-2

Wolę użyć findIndex()metody:

 var index = myArray.findIndex('hello','stevie');

index poda ci numer indeksu.

Monika Garg
źródło
1
Odpowiedź, pisownia i wcięcia kodu i :) są błędne?
Sachin Verma
1
findIndex nie jest w żadnej standardowej implementacji javascript. Nadchodzi propozycja (ecma 6) takiej metody, ale jej podpis nie jest taki. Wyjaśnij, co masz na myśli (być może nazwę metody), podaj deklarację metody findIndex lub nazwij używaną bibliotekę.
Sebastien F.