Pobierz indeks obiektu w tablicy, spełniając warunek

321

Mam taką tablicę:

[{prop1:"abc",prop2:"qwe"},{prop1:"bnmb",prop2:"yutu"},{prop1:"zxvz",prop2:"qwrq"},...]

Jak mogę uzyskać indeks obiektu, który pasuje do warunku, bez iteracji po całej tablicy?

Na przykład, biorąc pod uwagę prop2=="yutu", chcę uzyskać indeks 1.

Widziałem, .indexOf()ale myślę, że jest używany do prostych tablic takich jak ["a1","a2",...]. Sprawdziłem również, $.grep()ale zwraca obiekty, a nie indeks.

amp
źródło

Odpowiedzi:

732

Począwszy od 2016 roku, powinieneś używać Array.findIndex(standard ES2015 / ES6) do tego:

a = [
  {prop1:"abc",prop2:"qwe"},
  {prop1:"bnmb",prop2:"yutu"},
  {prop1:"zxvz",prop2:"qwrq"}];
    
index = a.findIndex(x => x.prop2 ==="yutu");

console.log(index);

Jest obsługiwany w Google Chrome, Firefox i Edge. W przypadku przeglądarki Internet Explorer na połączonej stronie znajduje się wypełnienie.

Uwaga dotycząca wydajności

Wywołania funkcji są drogie, dlatego przy naprawdę dużych tablicach prosta pętla będzie działać znacznie lepiej niż findIndex:

let test = [];

for (let i = 0; i < 1e6; i++)
    test.push({prop: i});


let search = test.length - 1;
let count = 100;

console.time('findIndex/predefined function');
    let fn = obj => obj.prop === search;

    for (let i = 0; i < count; i++)
        test.findIndex(fn);
console.timeEnd('findIndex/predefined function');


console.time('findIndex/dynamic function');
    for (let i = 0; i < count; i++)
        test.findIndex(obj => obj.prop === search);
console.timeEnd('findIndex/dynamic function');


console.time('loop');
    for (let i = 0; i < count; i++) {
        for (let index = 0; index < test.length; index++) {
            if (test[index].prop === search) {
                break;
            }
        }
    }
console.timeEnd('loop');

Podobnie jak w przypadku większości optymalizacji, należy to stosować ostrożnie i tylko wtedy, gdy jest to rzeczywiście potrzebne.

Georg
źródło
3
Nie widzę tutaj potrzeby tymczasowej tablicy. Wystarczy użyć faktu, że funkcja iteratora zamyka się w kontekście i użyć zmiennej. Ponadto wersja inna niż jQuery nie działa (przypuśćmy, że znaleziono ją w indeksie 0?). Oba rozwiązania wykonują więcej iteracji niż jest to wymagane, co jest mniej niż idealne, jeśli tablica jest duża (chociaż szanse na to, że będzie tak duża, że ​​człowiek zauważy, są niskie, chyba że dużo się dzieje ).
TJ Crowder
@ thg435: Nadal myślę, że to trochę maszyna Rube Goldberg, w której wystarczy prosta dźwignia. :-) Ale hej, to działa!
TJ Crowder
4
Czy możesz wyjaśnić, jak x => x.prop2=="yutu"działa funkcja findIndex ()?
Abhay Pai
5
@AbhayPai: to to samo, cofunction(x) { return x.prop2=="yutu" }
Georg
6
Podoba mi się sugestia, aby użyć poliolu. Jednak kod, który został napisany, nadal nie działa w IE11, nawet przy wypełnianiu wielokrotnym z powodu użycia funkcji strzałki / lambda. Re napisany jako index = a.findIndex(function (x) { return x.prop2 == "yutu" })stały problem tak, że wraz z kodem polyfill, findIndex pracował w IE11
Rick Glos
26

Jak mogę uzyskać indeks obiektu, który pasuje do warunku (bez iteracji wzdłuż tablicy)?

Nie możesz, coś musi iterować przez tablicę (przynajmniej raz).

Jeśli warunek bardzo się zmienia, musisz przejść przez pętlę i spojrzeć na znajdujące się w nim obiekty, aby sprawdzić, czy pasują do tego warunku. Jednak w systemie z funkcjami ES5 (lub jeśli zainstalujesz podkładkę), iterację można wykonać dość zwięźle:

var index;
yourArray.some(function(entry, i) {
    if (entry.prop2 == "yutu") {
        index = i;
        return true;
    }
});

Który używa nowego (ish) Array#somefunkcję , która przechodzi poprzez wpisy w tablicy aż do funkcji dajesz to zwraca true. Podana funkcja zapisuje indeks pasującego wpisu, a następnie wraca, trueaby zatrzymać iterację.

Lub oczywiście po prostu użyj forpętli. Różne opcje iteracji zostały omówione w tej drugiej odpowiedzi .

Ale jeśli zawsze będziesz używać tej samej właściwości do tego wyszukiwania, a jeśli wartości właściwości są unikalne, możesz zapętlić tylko raz i utworzyć obiekt, aby je odwzorować:

var prop2map = {};
yourArray.forEach(function(entry) {
    prop2map[entry.prop2] = entry;
});

(Lub ponownie możesz użyć forpętli lub dowolnej innej opcji ).

Jeśli chcesz znaleźć wpis prop2 = "yutu", możesz to zrobić:

var entry = prop2map["yutu"];

Nazywam to „indeksowaniem krzyżowym” tablicą. Oczywiście, jeśli usuniesz lub dodasz wpisy (lub zmienisz ich prop2wartości), musisz również zaktualizować obiekt mapowania.

TJ Crowder
źródło
Dziękuję za wyjaśnienie! Wskazane przez nas rozwiązanie z jQuery thg435zrobiło to, co chciałem ...
amp.
21

To, co powiedział TJ Crowder, za każdym razem będzie miało jakąś ukrytą iterację, z laszaszem staje się to:

var index = _.findIndex(array, {prop2: 'yutu'})
Aliak
źródło
1
podczas gdy możesz po prostu przeglądać różne sposoby uzyskania indeksu, znajdź Indeks jest najlepszym rozwiązaniem, nawet przyjętym w ES6 do metod macierzy rodzimych
Kelly Milligan,
13
var CarId = 23;

//x.VehicleId property to match in the object array
var carIndex = CarsList.map(function (x) { return x.VehicleId; }).indexOf(CarId);

W przypadku podstawowych numerów tablic możesz to również zrobić:

var numberList = [100,200,300,400,500];
var index = numberList.indexOf(200); // 1

Otrzymasz -1, jeśli nie może znaleźć wartości w tablicy.

David Castro
źródło
11
var index;
yourArray.some(function (elem, i) {
    return elem.prop2 === 'yutu' ? (index = i, true) : false;
});

Iteruj po wszystkich elementach tablicy. Zwraca indeks i true lub false, jeśli warunek nie jest zgodny.

Ważna jest jawna wartość zwracana true (lub wartość, której wynik boolowski jest prawdziwy). Pojedyncze przypisanie nie jest wystarczające z powodu możliwego indeksu z 0 (Boolean (0) === false), co nie spowodowałoby błędu, ale wyłącza przerwanie iteracji.

Edytować

Jeszcze krótsza wersja powyższego:

yourArray.some(function (elem, i) {
    return elem.prop2 === 'yutu' && ~(index = i);
});
Nina Scholz
źródło
Co ~ postać robi w twoim drugim fragmencie?
serkan
@serkan, jest to bitowy| operator NIE , to krótka wersja uzyskiwania z indeksu (z -1) wyniku prawda / fałsz , jeśli indeks istnieje.
Nina Scholz
dzięki Nina, bez znaku ~ kod działa tak, jak jest, prawda?
serkan
@serkan, twoje pytanie nie jest jasne, ale bez ~niego tak nie działa.
Nina Scholz
1
och, !!(index = 0)i !!~(index = 0)rzeczywiście różnica. Dzięki!
serkan
4

Możesz użyć Array.prototype.some () w następujący sposób (jak wspomniano w innych odpowiedziach):

https://jsfiddle.net/h1d69exj/2/

function findIndexInData(data, property, value) {
    var result = -1;
    data.some(function (item, i) {
        if (item[property] === value) {
            result = i;
            return true;
        }
    });
    return result;
}
var data = [{prop1:"abc",prop2:"qwe"},{prop1:"bnmb",prop2:"yutu"},{prop1:"zxvz",prop2:"qwrq"}]



alert(findIndexInData(data, 'prop2', "yutu")); // shows index of 1
GibboK
źródło
4

Widziałem wiele rozwiązań powyżej.

Korzystam z funkcji mapowania, aby znaleźć indeks wyszukiwanego tekstu w obiekcie tablicowym.

Wyjaśnię moją odpowiedź za pomocą danych uczniów.

  • krok 1 : utwórz obiekt tablicowy dla uczniów (opcjonalnie możesz stworzyć własny obiekt tablicowy).
    var students = [{name:"Rambabu",htno:"1245"},{name:"Divya",htno:"1246"},{name:"poojitha",htno:"1247"},{name:"magitha",htno:"1248"}];

  • krok 2 : Utwórz zmienną, aby wyszukać tekst
    var studentNameToSearch = "Divya";

  • krok 3 : Utwórz zmienną do przechowywania dopasowanego indeksu (tutaj używamy funkcji mapy do iteracji).
    var matchedIndex = students.map(function (obj) { return obj.name; }).indexOf(studentNameToSearch);

var students = [{name:"Rambabu",htno:"1245"},{name:"Divya",htno:"1246"},{name:"poojitha",htno:"1247"},{name:"magitha",htno:"1248"}];

var studentNameToSearch = "Divya";

var matchedIndex = students.map(function (obj) { return obj.name; }).indexOf(studentNameToSearch);

console.log(matchedIndex);

alert("Your search name index in array is:"+matchedIndex)

Rambabu Bommisetti
źródło
3
function findIndexByKeyValue(_array, key, value) {
    for (var i = 0; i < _array.length; i++) { 
        if (_array[i][key] == value) {
            return i;
        }
    }
    return -1;
}
var a = [
    {prop1:"abc",prop2:"qwe"},
    {prop1:"bnmb",prop2:"yutu"},
    {prop1:"zxvz",prop2:"qwrq"}];
var index = findIndexByKeyValue(a, 'prop2', 'yutu');
console.log(index);
pranabesh chand
źródło
1

Dlaczego nie chcesz dokładnie iterować? Nowy Array.prototype.forEach doskonale nadaje się do tego celu!

Możesz użyć drzewa wyszukiwania binarnego, aby znaleźć jedno wywołanie metody, jeśli chcesz. To fajna implementacja drzewa BTree i Red Black Search w JS - https://github.com/vadimg/js_bintrees - ale nie jestem pewien, czy można znaleźć indeks w tym samym czasie.

Rishabh
źródło
1

Jeden krok za pomocą Array.reduce () - bez jQuery

var items = [{id: 331}, {id: 220}, {id: 872}];

var searchIndexForId = 220;
var index = items.reduce(function(searchIndex, item, index){
  if(item.id === searchIndexForId) { 
    console.log('found!');
    searchIndex = index;
  }
  return searchIndex;
}, null);

zwróci, nulljeśli indeks nie zostanie znaleziony.

SagiSergeNadir
źródło
0
var list =  [
                {prop1:"abc",prop2:"qwe"},
                {prop1:"bnmb",prop2:"yutu"},
                {prop1:"zxvz",prop2:"qwrq"}
            ];

var findProp = p => {
    var index = -1;
    $.each(list, (i, o) => {
        if(o.prop2 == p) {
            index = i;
            return false; // break
        }
    });
    return index; // -1 == not found, else == index
}
Ruben Morales Felix
źródło
0

Georg wspomniał już, że ES6 ma Array.findIndex do tego. I niektóre inne odpowiedzi są obejścia dla ES5 przy użyciu metody Array.some.

Może być jeszcze jedno eleganckie podejście

var index;
for(index = yourArray.length; index-- > 0 && yourArray[index].prop2 !== "yutu";);

Jednocześnie chciałbym podkreślić, że Array.some może być implementowany za pomocą binarnej lub innej wydajnej techniki wyszukiwania. Może więc działać lepiej w pętli for w niektórych przeglądarkach.

Sanjoy
źródło
0

Wypróbuj ten kod

var x = [{prop1:"abc",prop2:"qwe"},{prop1:"bnmb",prop2:"yutu"},{prop1:"zxvz",prop2:"qwrq"}]
let index = x.findIndex(x => x.prop1 === 'zxvz')
Trilok Singh
źródło