Jak uzyskać indeks obiektu według jego właściwości w JavaScript?


241

Mam na przykład:

var Data = [
  { id_list: 1, name: 'Nick', token: '312312' },
  { id_list: 2, name: 'John', token: '123123' },
]

Następnie chcę na przykład posortować / odwrócić ten obiekt name. A potem chcę uzyskać coś takiego:

var Data = [
  { id_list: 2, name: 'John', token: '123123' },
  { id_list: 1, name: 'Nick', token: '312312' },
]

A teraz chcę poznać indeks obiektu z właściwością, name='John'aby uzyskać wartość tokena właściwości.

Jak rozwiązać problem?


Dlaczego najpierw chcesz posortować listę przed wyszukaniem nieruchomości?
JJJ

Obiekty JavaScript są {Key:Value}, naprawiłem to dla ciebie.
Rocket Hazmat


Jeśli przejrzysz odpowiedzi, wygląda na to, że istnieje jakiś Dataobiekt natywny . Jest to jedynie nazwa dużej litery, co jest sprzeczne z konwencją. Jeśli komuś to przeszkadza, dokonam edycji pytania i odpowiedzi, aby naprawić to nazewnictwo.
Roy Prins,

Odpowiedzi:


170

Jak sugerują inne odpowiedzi, zapętlanie tablicy jest prawdopodobnie najlepszym sposobem. Ale ustawiłbym to na swoją funkcję i uczyniłbym to trochę bardziej abstrakcyjnym:

function findWithAttr(array, attr, value) {
    for(var i = 0; i < array.length; i += 1) {
        if(array[i][attr] === value) {
            return i;
        }
    }
    return -1;
}

var Data = [
    {id_list: 2, name: 'John', token: '123123'},
    {id_list: 1, name: 'Nick', token: '312312'}
];

Dzięki temu nie tylko możesz znaleźć, który zawiera „John”, ale możesz również znaleźć, który zawiera token „312312”:

findWithAttr(Data, 'name', 'John'); // returns 0
findWithAttr(Data, 'token', '312312'); // returns 1
findWithAttr(Data, 'id_list', '10'); // returns -1

EDYCJA: Zaktualizowano funkcję, aby zwracała -1, gdy nie została znaleziona, dzięki czemu działa zgodnie z tą samą konstrukcją, co Array.prototype.indexOf ()


1
Dodano odpowiedź rozszerzającą to na wyszukiwanie głębsze niż jeden poziom atrybutu. Dzięki za to Chris - naprawdę pomógł: D
Ed Shee

3
Tym, którzy będą mieli problem z tym rozwiązaniem, pamiętaj, że znak równości === nie tylko sprawdzi wartość, ale także typ danych. więc porównanie daty, liczb i pustych wartości może zwrócić wartość false, jeśli rzeczywista wartość jest łańcuchem. Możesz użyć == znak równości, jeśli typ danych nie ma dla ciebie żadnego znaczenia.
David Addoteye

NIE Rób tego. Dowiedz się, jak korzystać z funkcji wyższego rzędu. Jest to stary sposób robienia rzeczy.
Led

327

Ponieważ część sortowania jest już odebrana. Mam zamiar zaproponować inny elegancki sposób uzyskania indeksu właściwości w tablicy

Twój przykład to:

var Data = [
    {id_list:1, name:'Nick',token:'312312'},
    {id_list:2,name:'John',token:'123123'}
]

Możesz to zrobić:

var index = Data.map(function(e) { return e.name; }).indexOf('Nick');

Array.prototype.mapnie jest dostępny w IE7 lub IE8. Kompatybilność z ES5

A oto z ES6 i składnią strzałek, która jest jeszcze prostsza:

const index = Data.map(e => e.name).indexOf('Nick');

4
Muszę iterować całą tablicę bez względu na wszystko. Pierwsza metoda nie musi.
Niemiecki Attanasio

1
Proste, ale należy wziąć pod uwagę wydajność przy użyciu dużych zestawów danych
gavioto,

1
Naprawdę świetna odpowiedź pomogła mi z tym problemem!
NiallMitch14

1
To rozwiązanie jest cudowne. Niech pan dożyje tysiąc lat, proszę pana.
deepelement

3
Wykonanie mapy, a następnie indexOf w wersji ES6 oznacza, że ​​zapętlą dwa razy tablicę. Zamiast tego powinieneś użyć metody findIndex pokazanej w mojej odpowiedzi
silverlight513

209

Jeśli nie masz nic przeciwko korzystaniu z ES6. Tablice mają teraz funkcję findIndex . Co oznacza, że ​​możesz zrobić coś takiego:

const index = Data.findIndex(item => item.name === 'John');

6
podaję najbardziej bezpośrednie i eleganckie rozwiązanie, chciałbym zrobić małe, że findIndex jest zachłanny - w moim przypadku był idealny - ale użytkownicy są świadomi, że jeśli masz obiekty o zduplikowanych wartościach (tj. dwa obiekty z name: John), to zwróci tylko pierwszy mecz
GrayedFox

Niesamowite!! Dziękuję Ci.
moreirapontocom

@GrayedFox nie miałeś na myśli, że NIE jesteś chciwy? Chciwy oznacza więcej wyników, a findIndex zwraca tylko pierwsze dopasowanie.
Jos

1
Przepraszamy @Jos, ale myślę, że nie do końca rozumiesz, co robi chciwy algorytm wyszukiwania . Chciwy algorytm bierze pod uwagę tylko posiadane informacje, które są wydajne obliczeniowo, powiedzmy, do wyszukiwania elementu na liście, gdy potrzebujesz tylko jednego wyniku. Metoda findIndex jest chciwa, ponieważ zwraca tylko pierwsze dopasowanie. Gdyby nie było chciwe, kontynuowałoby wyszukiwanie. Zastanawiasz się nad terminem „chciwy”, ponieważ jest używany w codziennym języku angielskim, ale w kontekście programowania (tj. SO) jest odwrotnością tego, co myślisz;)
GrayedFox,

Dzięki za wyjaśnienie @GrayedFox. Moje odniesienie do chciwości pochodziło też od programowania, ale odnosiło się do wyrażeń regularnych, w których chciwość wymaga więcej znaków niż nie chciwość! :-)
Jos.

28
var index = Data.findIndex(item => item.name == "John")

Która jest uproszczoną wersją:

var index = Data.findIndex(function(item){ return item.name == "John"})

Z mozilla.org:

Metoda findIndex () zwraca indeks pierwszego elementu w tablicy, który spełnia podaną funkcję testową. W przeciwnym razie zwracane jest -1.


2
Użyj „===” do porównania ciągów, jedyne czego brakuje w tej odpowiedzi.
Bruno Tavares

@BrunoTavares, który według Ciebie scenariusz byłby inny w tym przypadku?
edank

1
@edank Jeśli OP miałby sprawdzić tokenpole zamiast namepola, mogą występować problemy, jak Data[0].token == 312312ocenia true, ale Data[0].token === 321321ocenia false.
kem

@edank spójrz w tej odpowiedzi stackoverflow.com/questions/359494/…
Bruno Tavares,

23

Jeśli masz problemy z IE, możesz użyć funkcji map (), która jest obsługiwana od wersji 9.0:

var index = Data.map(item => item.name).indexOf("Nick");

1
Poniższa odpowiedź zawiera więcej informacji i została opublikowana jako pierwsza.
Alexander

To zdecydowanie najlepsze rozwiązanie.
Bebolicious

4

Jedynym znanym mi sposobem jest zapętlanie całej tablicy:

var index=-1;
for(var i=0;i<Data.length;i++)
  if(Data[i].name==="John"){index=i;break;}

lub bez rozróżniania wielkości liter:

var index=-1;
for(var i=0;i<Data.length;i++)
  if(Data[i].name.toLowerCase()==="john"){index=i;break;}

W wyniku zmienny indeks zawiera indeks obiektu lub -1, jeśli nie został znaleziony.


2

prototypowy sposób

(function(){
  if (!Array.prototype.indexOfPropertyValue){
    Array.prototype.indexOfPropertyValue = function(prop,value){
      for (var index = 0; index < this.length; index++){
        if (this[index][prop]){
          if (this[index][prop] == value){
            return index;
          }
        }
      }
      return -1;
    }
  }
 })();
 // usage:
 var Data = [
 {id_list:1, name:'Nick',token:'312312'},{id_list:2,name:'John',token:'123123'}];
 Data.indexOfPropertyValue('name','John'); // returns 1 (index of array);
 Data.indexOfPropertyValue('name','Invalid name') // returns -1 (no result);
 var indexOfArray = Data.indexOfPropertyValue('name','John');
 Data[indexOfArray] // returns desired object.

Musiałem to trochę zmienić, aby działało w mojej sytuacji - szukałem indeksu właściwości o wartości null, a piąta linia powodowała, że ​​przeskakiwał on nad tym indeksem.
David Brunow

1

Możesz użyć Array.sort za pomocą funkcji niestandardowej jako parametru do zdefiniowania mechanizmu sortowania.

W twoim przykładzie dałoby to:

var Data = [
    {id_list:1, name:'Nick',token:'312312'},{id_list:2,name:'John',token:'123123'}
]

Data.sort(function(a, b){
    return a.name < b.name ? -1 : a.name > b.name ? 1 : 0;
});

alert("First name is : " + Data[0].name); // alerts 'John'
alert("Second name is : " + Data[1].name); // alerts 'Nick'

Funkcja sortowania musi zwrócić albo -1, jeśli a powinno wystąpić przed b, 1, jeśli a powinno nastąpić po b, i 0, jeśli oba są równe. Do Ciebie należy określenie właściwej logiki w funkcji sortowania, aby posortować tablicę.

Edycja: pominięto ostatnią część pytania, w której chcesz poznać indeks. Trzeba będzie zapętlić tablicę, aby znaleźć to, jak powiedzieli inni.


1

Wystarczy przejrzeć tablicę i znaleźć pozycję:

var i = 0;
for(var item in Data) {
    if(Data[item].name == 'John')
        break;
    i++;
}
alert(i);

To powinno być if(Data[item].name == 'John'), naprawiłem to dla ciebie.
Rocket Hazmat

To jest chwila. Jeśli „nie znaleziono” zmienna i zawiera indeks pozytywny. Aby przetestować „nie znaleziono”, należy porównać, czy i === Data.length
Andrew D.

Drugi moment to sytuacja, gdy tablica zawiera niestandardowe właściwości nieindeksujące. Na przykład: var Data [1,2,3,4,5]; Dane [„test”] = 7897; Porównaj dane wyjściowe: dla alertu (var i in Data) (Data [i]); i dla (var i = 0; i <Data.length; i ++) alert (Data [i]);
Andrew D.,


1

Dlaczego nie używasz małego obejścia?

Utwórz nową tablicę z nazwami jako indeksami. potem wszystkie wyszukiwania będą używać indeksów. Tak więc tylko jedna pętla. Po tym nie musisz zapętlać wszystkich elementów!

var Data = [
    {id_list:1, name:'Nick',token:'312312'},{id_list:2,name:'John',token:'123123'}
    ]
var searchArr = []
Data.forEach(function(one){
  searchArr[one.name]=one;
})
console.log(searchArr['Nick'])

http://jsbin.com/xibala/1/edit

przykład na żywo.


Oznacza to, że najpierw przechodzisz przez wszystko ... a potem modyfikujesz obiekty (chyba że je sklonujesz, ale to jeszcze więcej). A potem znów się zapętla (przynajmniej częściowo).
Jos

1

Rozszerzona odpowiedź Chrisa Picketta, ponieważ w moim przypadku musiałem szukać głębiej niż jeden poziom atrybutu:

function findWithAttr(array, attr, value) {
  if (attr.indexOf('.') >= 0) {
    var split = attr.split('.');
    var attr1 = split[0];
    var attr2 = split[1];
    for(var i = 0; i < array.length; i += 1) {
      if(array[i][attr1][attr2] === value) {
        return i;
      }
    }
  } else {
    for(var i = 0; i < array.length; i += 1) {
      if(array[i][attr] === value) {
        return i;
      }
    }
  };
};

Możesz przekazać „attr1.attr2” do funkcji


1

A co z tym ? :

Data.indexOf(_.find(Data, function(element) {
  return element.name === 'John';
}));

Zakładając, że używasz lodash lub underscorejs.


1
var fields = {
teste:
{
    Acess:
    {
        Edit: true,
      View: false

      }
 },
teste1:
{
    Acess:
    {
        Edit: false,
      View: false

      }
  }
};

console.log(find(fields,'teste'));
function find(fields,field){
    for(key in fields){
        if(key == field){
        return true;
    }
}
return false;
}

Jeśli masz jeden obiekt z wieloma obiektami w środku, jeśli chcesz wiedzieć, czy jakiś obiekt znajduje się w obiekcie głównym, po prostu wstaw find (MasterObject, 'Object to Search'), ta funkcja zwróci odpowiedź, jeśli istnieje lub nie (PRAWDA lub FAŁSZ ), Mam nadzieję, że pomogę w tym, mogę zobaczyć przykład na JSFiddle .


Dodaj wyjaśnienie wraz z odpowiedzią, w jaki sposób ta odpowiedź pomaga OP w rozwiązaniu bieżącego problemu
ρяσсρѕя K

@ ρяσсρєяK, Dziękuję za sugestię, już dodałem wyjaśnienie :)
Pedro Monteiro Arantes

1
let indexOf = -1;
let theProperty = "value"
let searchFor = "something";

theArray.every(function (element, index) {

    if (element[theProperty] === searchFor) {
        indexOf = index;
        return false;
    }
    return true;
});

1
collection.findIndex(item => item.value === 'smth') !== -1

Ten fragment kodu może rozwiązać pytanie, ale wyjaśnienie naprawdę pomaga poprawić jakość posta. Pamiętaj, że w przyszłości odpowiadasz na pytanie dla czytelników, a ci ludzie mogą nie znać przyczyn Twojej sugestii kodu. Staraj się również nie tłoczyć kodu objaśniającymi komentarzami, ponieważ zmniejsza to czytelność zarówno kodu, jak i objaśnień!
Do widzenia StackExchange

1

Jeśli chcesz uzyskać wartość tokena właściwości, możesz także spróbować tego

    let data=[
      { id_list: 1, name: 'Nick', token: '312312' },
      { id_list: 2, name: 'John', token: '123123' },
    ]

    let resultingToken =  data[_.findKey(data,['name','John'])].token

gdzie _.findKey jest funkcją loży


0

Alternatywnie do odpowiedzi niemieckiego Attanasio Ruiza możesz wyeliminować drugą pętlę, używając Array.reduce () zamiast Array.map ();

var Data = [
    { name: 'hypno7oad' }
]
var indexOfTarget = Data.reduce(function (indexOfTarget, element, currentIndex) {
    return (element.name === 'hypno7oad') ? currentIndex : indexOfTarget;
}, -1);

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.