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ę!

Petrov
źródło
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];
Pablo Francisco Pérez Hidalgo
źródło
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)

Umair Ahmed
źródło
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.

jbabey
źródło
+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);
PirateApp
źródło
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.

Nina Scholz
źródło
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);
    }
}
Elliot Bonneville
źródło
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
enapupe
źródło
3

Nowy sposób korzystania z ES6

let picked_element = array.filter(element => element.id === 0);
Silve2611
źródło
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);

PulpDood
źródło
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
Tejs
źródło
2

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

matchingIndices.push(j);

do

matchingIndices.push(NumberInt(j+1));
user2584621
źródło
2

Korzystanie z mapfunkcji ES6 :

let idToFind = 3;
let index = someArray.map(obj => obj.id).indexOf(idToFind);
JoeTidee
źródło
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, [])

trungk18
źródło
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.

Xander N
źródło
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'}
patotoma
źródło
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)

skrajny
źródło
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 );
Daniel Lefebvre
źródło