W tablicy obiektów najszybszy sposób na znalezienie indeksu obiektu, którego atrybuty pasują do wyszukiwania


135

Trochę surfowałem po okolicy, próbując znaleźć skuteczny sposób, aby to zrobić, ale do niczego nie doszedłem. Mam tablicę obiektów, która wygląda następująco:

array[i].id = some number;
array[i].name = some name;

To, co chcę zrobić, to znaleźć INDEKSY obiektów, w których id jest równe, na przykład, jednemu z 0, 1, 2, 3 lub 4. Przypuszczam, że mógłbym zrobić coś takiego:

var indexes = [];
for(i=0; i<array.length; i++) {
  (array[i].id === 0) ? { indexes[0] = i }
  (array[i].id === 1) ? { indexes[1] = i }
  (array[i].id === 2) ? { indexes[2] = i }
  (array[i].id === 3) ? { indexes[3] = i }
  (array[i].id === 4) ? { indexes[4] = i }
}

Chociaż to by zadziałało, wygląda na dość drogie i wolne (nie wspominając o brzydkim), zwłaszcza jeśli array.length może być duża. Jakieś pomysły, jak to trochę urozmaicić? Myślałem o użyciu w jakiś sposób array.indexOf, ale nie wiem, jak wymusić składnię. To

array.indexOf(this.id === 0);

na przykład zwraca undefined, tak jak prawdopodobnie powinno. Z góry dziękuję!


1
Jeśli masz zwykłą starą tablicę, wszystko, co możesz zrobić, to iterować. To właśnie są tablice, zbiór obiektów uporządkowanych według indeksu tablicy.
Dave Newton

2
Wystarczy, że dziś natkniesz się na ten post, dla wszystkich spóźnialskich dostępna jest nowa metoda tablicowa Array.prototype.findIndex()w ECMAScript 2015. Zaakceptowana odpowiedź była jednak niesamowita.
Conrad Lo

Jestem fanem składni ES6 (użyj polyfillów, jeśli potrzebna jest obsługa starszych przeglądarek). ES7 + ES8 będą przyszłością
Fr0zenFyr

Odpowiedzi:


391

Może chciałbyś użyć funkcji wyższego rzędu, takich jak „mapa”. Zakładając, że chcesz wyszukiwać według atrybutu „pole”:

var elementPos = array.map(function(x) {return x.id; }).indexOf(idYourAreLookingFor);
var objectFound = array[elementPos];

9
Ta odpowiedź jest świetna, ponieważ faktycznie odpowiada na pytanie, podając indeks :)
przeciwstawianie się

3
@ZeroAbsolute Twoja zastosowana funkcja (przekazana do mapy) może zwrócić ciąg skrótu, który powinien zapewniać unikalny klucz dla każdej możliwej kombinacji podanej przez twoje kryteria. Na przykład: function hashf(el) { return String(el.id) + "_" + String(el.name); }. To tylko wskazówka: elementPos = array.map(hashf(x)).indexOf(hash({id:3, name:'Pablo'}));oczywiście funkcja skrótu, którą zapewniam, nie jest poprawna we wszystkich przypadkach, ponieważ '_'może stanowić część twoich wartości, ale jest to tylko szybki przykład, możesz znaleźć różne metody mieszania.
Pablo Francisco Pérez Hidalgo

1
Co to zwraca, jeśli nie zostanie znalezione? Zakładam -1, po prostu ciekawy. Będę eksperymentować.
Nathan C. Tresch

1
@ NathanC.Tresch Zwraca -1, ponieważ jest to indexOfwartość zwracana, gdy nie może zlokalizować podanej wartości.
Pablo Francisco Pérez Hidalgo

2
Cześć wszystkim, zamiast używać dwóch metod map, indexOf, możesz użyć tylko jednej o nazwie findIndex....... Np .:[{id:1},{id:2},{id:3},{id:4}].findIndex(function(obj){return obj.id == 3}) OR [{id:1},{id:2},{id:3},{id:4}].findIndex(obj => obj.id == 3)
Umair Ahmed

64

Najprostszy i najłatwiejszy sposób na znalezienie indeksu elementu w tablicy.

Składnia ES5: [{id:1},{id:2},{id:3},{id:4}].findIndex(function(obj){return obj.id == 3})

Składnia ES6: [{id:1},{id:2},{id:3},{id:4}].findIndex(obj => obj.id == 3)


4
Uważam, że to najbardziej eleganckie rozwiązanie. Dla tych, którzy martwią się o kompatybilność wsteczną, możesz znaleźć wypełnienie findIndexna stronie developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/ ...
mrogers

2
Otrzymuję ostrzeżenie w moim narzędziu lint ES6, że obj.id == 3operator użyty tutaj może spowodować nieoczekiwaną konwersję typu, więc obj.id === 3zamiast tego użyj operatora, który sprawdza równą wartość i typ.
thclark

1
Ta odpowiedź jest co najmniej 3,5 razy szybsza niż zaakceptowana powyżej odpowiedź. Użycie var elementPos = array.map(function(x) {return x.id; }).indexOf(idYourAreLookingFor);tego zajęło 0,03500000002532033 milisekund. Użycie [{id:1},{id:2},{id:3},{id:4}].findIndex(obj => obj.id == 3)tego zajęło 0,00999999747378752 milisekund.
Ovidio Reyna

1
TA ODPOWIEDŹ jest najbardziej WYDAJNA, ponieważ nie iteruje całej tablicy. Wybrana odpowiedź zmapuje całą tablicę, a następnie findIndex, który jest zobowiązany do jednorazowego iteracji całej tablicy
Karun

26

Nowa metoda Array .filter () będzie dobrze działać w tym przypadku:

var filteredArray = array.filter(function (element) { 
    return element.id === 0;
});

jQuery może to również zrobić za pomocą .grep ()

edycja: warto wspomnieć, że obie te funkcje po prostu powtarzają się pod maską, nie będzie zauważalnej różnicy w wydajności między nimi i włączaniem własnej funkcji filtra, ale po co wymyślać koło na nowo.


+1, zawsze zapominam o takich wbudowanych funkcjach na obiektach.
Tejs

59
To nie zwraca indeksu.
Adam Grant

To nie odpowiada na to konkretne pytanie, ale bardzo mi pomóż! Dzięki!
rochasdv

To nie zwraca indeksu.
Rich

10

Jeśli zależy Ci na wydajności, nie idź z znalezisku lub filtr lub map lub którykolwiek z wyżej omówionych metod

Oto przykład demonstrujący najszybszą metodę. TUTAJ jest link do właściwego testu

Blok konfiguracji

var items = []

for(var i = 0; i < 1000; i++) {
    items.push({id: i + 1})
}

var find = 523

Najszybsza metoda

var index = -1
for(var i = 0; i < items.length; i++) {
    if(items[i].id === find) {
        index = i;
        break;
    }
}

Wolniejsze metody

items.findIndex(item => item.id === find)

NAJWOLNIEJSZA metoda

items.map(item => item.id).indexOf(find);

2
Dziękujemy za udostępnienie tego porównania! Bardzo interesujące jest to, jak bardzo różni się wydajność - w tym, która metoda jest szybsza, w zależności od przeglądarki / silnika JavaScript używanego do ich uruchamiania.
Iain Collins

1
Myślę, że należy to zaznaczyć jako odpowiedź. To pokazuje najszybszą i wolniejszą drogę.
Painkiller

W twoim benchmarku blok 2 (przy użyciu findIndex) jest dla mnie szybszy (na Microsoft Edge Chromium 83.0.474.0)
rezadru

Block 2 jest teraz szybszy również na chrome
Cody Mikol

8
array.forEach(function (elem, i) {  // iterate over all elements of array
    indexes[elem.id] = i;           // take the found id as index for the
});                                 // indexes array and assign i

wynikiem jest lista wyszukiwania identyfikatora. z podanym id otrzymujemy indeks rekordu.


6
var indices = [];
var IDs = [0, 1, 2, 3, 4];

for(var i = 0, len = array.length; i < len; i++) {
    for(var j = 0; j < IDs.length; j++) {
        if(array[i].id == ID) indices.push(i);
    }
}

6

Ponieważ nie ma odpowiedzi przy użyciu zwykłej tablicy find:

var one = {id: 1, name: 'one'};
var two = {id: 2, name:'two'}
var arr = [one, two] 

var found = arr.find((a) => a.id === 2)

found === two // true

arr.indexOf(found) // 1

3

Nowy sposób korzystania z ES6

let picked_element = array.filter(element => element.id === 0);

picked_elementto tablica w tym przypadku ...
Heretic Monkey

3

const index = array.findIndex(item => item.id === 'your-id');

To powinno dać ci indeks pozycji w tablicy z id === twój-id

array = [ {id:1}, {id:2} ];

const index = array.findIndex(item => item.id === 2);

console.log(index);


2

Wydaje mi się, że możesz stworzyć prosty iterator z wywołaniem zwrotnym do testowania. Tak jak to:

function findElements(array, predicate)
{
    var matchingIndices = [];

    for(var j = 0; j < array.length; j++)
    {
        if(predicate(array[j]))
           matchingIndices.push(j);
    }

    return matchingIndices;
}

Następnie możesz wywołać w ten sposób:

var someArray = [
     { id: 1, text: "Hello" },
     { id: 2, text: "World" },
     { id: 3, text: "Sup" },
     { id: 4, text: "Dawg" }
  ];

var matchingIndices = findElements(someArray, function(item)
   {
        return item.id % 2 == 0;
   });

// Should have an array of [1, 3] as the indexes that matched

2

Dostosowując odpowiedź Tejsa do mongoDB i Robomongo zmieniłem

matchingIndices.push(j);

do

matchingIndices.push(NumberInt(j+1));

2

Korzystanie z mapfunkcji ES6 :

let idToFind = 3;
let index = someArray.map(obj => obj.id).indexOf(idToFind);

2

Podsumowując całą świetną odpowiedź powyżej i dodatkową moją odpowiedź dotyczącą znaleźć wszystkie indeksy pojawiły się z niektórych komentarzy.

  1. Aby zwrócić indeks pierwszego wystąpienia.

const array = [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }, { id: 2 }];
const idYourAreLookingFor = 2;

//ES5 
//Output: 1
array.map(function (x) { return x.id; }).indexOf(idYourAreLookingFor);

//ES6 
//Output: 1
array.findIndex(obj => obj.id === idYourAreLookingFor);

  1. Aby zwrócić tablicę indeksów wszystkich wystąpień, użyj funkcji redukuj.

const array = [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }, { id: 2 }]
const idYourAreLookingFor = 2;

//ES5
//Output: [1, 4]
array.reduce(function (acc, obj, i) {
  if (obj.id === idYourAreLookingFor)
    acc.push(i);
  return acc;
}, []);

//ES6
//Output: [1, 4]
array.reduce((acc, obj, i) => (obj.id === idYourAreLookingFor) ? acc.concat(i) : acc, [])


0

Ponieważ nie mogę jeszcze komentować, chcę pokazać rozwiązanie, które zastosowałem w oparciu o metodę opublikowaną przez Umair Ahmed, ale gdy chcesz wyszukać klucz zamiast wartości:

[{"a":true}, {"f":true}, {"g":false}]
.findIndex(function(element){return Object.keys(element)[0] == "g"});

Rozumiem, że nie odpowiada na rozszerzone pytanie, ale tytuł nie określa, czego oczekiwano od każdego obiektu, więc chcę pokornie się tym podzielić, aby w przyszłości oszczędzić innym bólom głowy, podczas gdy ja nie rozumiem, że może to nie być najszybsze rozwiązanie.


0

Stworzyłem małe narzędzie o nazwie super-array, w którym można uzyskać dostęp do elementów w tablicy za pomocą unikalnego identyfikatora o złożoności O (1). Przykład:

const SuperArray = require('super-array');

const myArray = new SuperArray([
  {id: 'ab1', name: 'John'},
  {id: 'ab2', name: 'Peter'},
]);

console.log(myArray.get('ab1')); // {id: 'ab1', name: 'John'}
console.log(myArray.get('ab2')); // {id: 'ab2', name: 'Peter'}

Możesz chcieć przeczytać Jak oferować osobiste biblioteki open source? przed opublikowaniem tego wszędzie.
Martijn Pieters

@MartijnPieters Opublikowałem to tylko na kilka istotnych pytań, a projekt jest wolny od MIT, więc o co chodzi? Może mógłbyś być trochę bardziej tolerancyjny.
patotoma

0
var test = [
  {id:1, test: 1},
  {id:2, test: 2},
  {id:2, test: 2}
];

var result = test.findIndex(findIndex, '2');

console.log(result);

function findIndex(object) {
  return object.id == this;
}

zwróci indeks 1 (działa tylko w ES 2016)


0

Podoba mi się ta metoda, ponieważ łatwo jest porównać ją z dowolną wartością w obiekcie, niezależnie od tego, jak głęboko jest on zagnieżdżony.

 while(i<myArray.length && myArray[i].data.value!==value){
  i++; 
}
// i now hows the index value for the match. 
 console.log("Index ->",i );
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.