metodo indexOf in un array di oggetti?


514

Qual è il metodo migliore per ottenere l'indice di un array che contiene oggetti?

Immagina questo scenario:

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

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

Ora vorrei avere l' indexOfoggetto quale helloproprietà è quella 'stevie'che, in questo esempio, sarebbe 1.

Sono un principiante con JavaScript e non so se esiste un metodo semplice o se dovrei creare la mia funzione per farlo.


1
Vuoi unire i due oggetti helloe qaz?
Armin,

No, non lo so. Voglio avere un elenco di oggetti in un array.
Antonio Laguna,

Ah ok! Si desidera conoscere la posizione dell'intero oggetto nell'array, che ha una proprietà definita.
Armin,

10
Ho trovato una funzione molto semplice per risolvere questo esatto problema con questa risposta 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 è meglio della risposta accettata (se ES6 funziona per te) - vedi l'esempio completo di seguito
anno 1

Risposte:


1045

Penso che puoi risolverlo in una riga usando la funzione mappa :

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

58
Questa dovrebbe essere onestamente la risposta accettata. La maggior parte dei browser al giorno d'oggi supportaArray.prototype.map()
AlbertEngelB,

10
Non è supportato da IE8 ma, se questo non è un problema, questa è la soluzione migliore.
Antonio Laguna,

57
Um ... non vale la pena notare che Array.prototype.map () crea un array completamente nuovo contenente gli elementi mappati? Quindi, se hai un array con 1000 elementi, hai prima creato un altro array con 1000 elementi, quindi cercalo? Vale la pena, penso, di vedere le prestazioni di questo metodo rispetto a un semplice ciclo. Soprattutto quando si esegue su una piattaforma mobile con risorse limitate.
Doug,

7
@Doug Sebbene il tuo punto sulle prestazioni sia effettivamente corretto, chi nella loro mente corretta sostituirebbe una riga di codice con sette per un'applicazione che è quasi per definizione IO / Rete associata fino a quando non avessero profilato per colli di bottiglia?
Jared Smith,

6
Tecnicamente un file js minimizzato è anche una riga; D
GreaterKing

371

Array.prototype.findIndex è supportato in tutti i browser diversi da IE (non edge). Ma il polyfill fornito è carino.

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

La soluzione con la mappa è a posto. Ma stai iterando sull'intero array ogni ricerca. Questo è solo il caso peggiore per findIndex che interrompe l'iterazione una volta trovata una corrispondenza.


Non c'è davvero un modo conciso (quando gli sviluppatori hanno dovuto preoccuparsi di IE8) , ma ecco una soluzione comune:

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

o come funzione:

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

Solo alcune note:

  1. Non usare per ... in loop su array
  2. Assicurati di uscire dal ciclo o tornare fuori dalla funzione una volta trovato il tuo "ago"
  3. Fai attenzione all'uguaglianza degli oggetti

Per esempio,

var a = {obj: 0};
var b = [a];
b.indexOf({obj: 0}); // -1 not found

la funzione ha un confronto tra searchterm errato come dovrebbe essere searchTerm :)
Antonio Laguna,

si sono verificati più eventi
Joe,

3
@SteveBennett è una versione ottimizzata per le prestazioni; la lunghezza dell'array deve essere determinata una sola volta (quando le variabili per il ciclo for sono inizializzate). Nel tuo caso, la lunghezza viene controllata di nuovo ogni iterazione. Vedi anche stackoverflow.com/questions/5349425/… e stackoverflow.com/questions/8452317/… Tuttavia, se le prestazioni non sono elevate, non importa.
Unother il

1
Buona risposta, ma ho fatto alcuni benchmark delle prestazioni (vedi jsperf.com/find-index-of-object-in-array-by-contents ) e ho scoperto che la risposta basata sulle funzioni menzionata qui sembra essere la seconda risposta più performante. L'unica cosa più performante finisce per metterlo in un prototipo, anziché in una funzione, come indicato nella mia risposta .
Uniphonic,

123

In ES2015, questo è abbastanza semplice:

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

o, probabilmente con prestazioni migliori per array più grandi:

myArray.findIndex(x => x.hello === 'stevie')

2
Buon approccio all'uso di ES6
kag

Sono rimasto sorpreso dal fatto che nessuno di questi metodi sia performante come il prototipo per loop, come indicato nella mia risposta. Anche se il supporto del browser del metodo findIndex è un po 'scarso, sembra che farebbe la stessa cosa, ma finirà per essere meno performante? Vedi il link nella mia risposta per i benchmark.
Uniphonic,

Buono a sapersi, se le prestazioni sono importanti per l'attività da svolgere. Molto raramente, nella mia esperienza, è ymmv.
Steve Bennett,

24
var idx = myArray.reduce( function( cur, val, index ){

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

}, -1 );

17

Mi piace la risposta di Pablo, ma Array # indexOf e Array # map non funzionano su tutti i browser. Underscore utilizzerà il codice nativo se disponibile, ma presenta anche fallback. Inoltre ha il metodo spennato per fare esattamente ciò che fa il metodo di mappa anonima di Pablo.

var idx = _.chain(myArray).pluck("hello").indexOf("Stevie").value();

1
Array.prototype.map () è soported per IE9 + ed è possibile utilizzare un Polyfill per IE8, 7, 6: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Johann Echavarria

1
Potresti usare un polyfill. . . oppure potresti semplicemente usare la sottolineatura o lo lodash, che sono fondamentalmente polyfill a cui sono attaccati un sacco di altre chicche . Qual è l'obiezione con il trattino basso? Dimensione?
tandrewnichols,

Mi piace molto Underscore, anche la tua risposta è intelligente, ma la risposta di IMHO Pablo è la più chiara.
Johann Echavarria,

Wow, non avrei mai pensato di usare il concatenamento in quel modo. Mi piace molto come rende fluida la ricerca.
Dylan Pierce,

chainè superfluo qui. _.pluck(myArray, 'hello').indexOf('stevie')
Steve Bennett,

14

O prototipalo:

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");

9
Molto conveniente! Anche se sceglierei prototype.indexOfObject in modo da non interferire con il metodo esistente 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
Lo avvolgo in una chiusura autoeseguita con il vecchio che viene archiviato in anticipo, con la prima riga della funzione di sostituzione che è qualcosa sulla falsariga di if (typeof property === 'string' || typeof property === 'number' || typeof property === 'boolean') return oldIndexOf(property, value);. Questo perché questi sono i pochi tipi immutabili. Vorrei anche presentare un terzo argomento per abilitare il fallback al metodo nativo, se necessario.
Isiah Meadows

8

Breve

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

Casi d'uso :

  /*****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);
      }
    };

6

Ho fatto alcuni test sulle prestazioni di varie risposte qui, che chiunque può eseguire da solo:

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

Sulla base dei miei test iniziali su Chrome, il seguente metodo (utilizzando un ciclo for impostato all'interno di un prototipo) è il più veloce:

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");

Questo codice è una versione leggermente modificata della risposta di Nathan Zaetta.

Nei benchmark delle prestazioni l'ho provato con sia l'obiettivo nel mezzo (indice 500) che molto fine (indice 999) di un array di 1000 oggetti, e anche se ho inserito il target come l'ultimo elemento dell'array (che significa che deve passare in rassegna ogni singolo elemento dell'array prima che venga trovato) finisce comunque il più veloce.

Questa soluzione ha anche il vantaggio di essere una delle più concise per l'esecuzione ripetuta, poiché solo l'ultima riga deve essere ripetuta:

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

2
Stavo per pubblicare una risposta a questa domanda usando un violino con i miei test, ma grazie alla tua risposta, non devo più. Voglio solo confermare i tuoi test: ho ottenuto lo stesso risultato, ma usando un whileloop anziché foruno, e performance.now(). Vorrei che questa risposta fosse più votata e che l'avessi vista prima, mi avrebbe risparmiato un po 'di tempo ...
Yin Cognyto


5

Mentre, la maggior parte delle altre risposte qui sono valide. A volte, è meglio fare una breve funzione semplice vicino a dove la userete.

// 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');

Dipende da ciò che è importante per te. Potrebbe salvare righe di codice ed essere molto intelligente nell'usare un one-liner o mettere da qualche parte una soluzione generica che copra vari casi limite. Ma a volte è meglio semplicemente dire: "qui l'ho fatto in questo modo" piuttosto che lasciare ai futuri sviluppatori un ulteriore lavoro di ingegneria inversa. Soprattutto se ti consideri "un principiante" come nella tua domanda.


4
array.filter(function(item, indx, arr){ return(item.hello === 'stevie'); })[0];

Attenzione al [0] .

È corretto usare reducecome inAntonio Laguna risposta.

Scuse per la brevità ...


4

Se il tuo oggetto è lo stesso oggetto di quello che stai usando nell'array, dovresti essere in grado di ottenere l'indice dell'oggetto nello stesso modo in cui lo fai come se fosse una stringa.

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

Questa è la risposta che cercavo perché non mi era chiaro se IndexOf corrispondesse per valore o cosa. Ora so che posso usare IndexOf per trovare il mio oggetto e non preoccuparmi se ci sono altri oggetti con le stesse proprietà.
MDave


3

semplice:

myArray.indexOf(myArray.filter(function(item) {
    return item.hello == "stevie"
})[0])

3

Se sei interessato solo a trovare la posizione, vedi la risposta di @ Pablo .

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

Tuttavia, se non vedi l'ora di trovare l'elemento (cioè se stavi pensando di fare qualcosa del genere myArray[pos]), c'è un modo più efficiente di una linea per farlo, usando filter.

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

Vedi i risultati delle prestazioni (~ + 42% ops / sec): http://jsbench.github.io/#7fa01f89a5dc5cc3bee79abfde80cdb3


2

Vedi questo esempio: http://jsfiddle.net/89C54/

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

Inizia a contare con zero.


2

Ho creato una funzione generica per verificare quanto segue è il codice e funziona per qualsiasi oggetto

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));

Il primo avviso restituirà -1 (significa che la corrispondenza non è stata trovata) e il secondo avviso restituirà 0 (significa che la corrispondenza è stata trovata).


2

Utilizzare _.findIndexdalla libreria underscore.js

Ecco l'esempio _.findIndex([{a:1},{a: 2,c:10},{a: 3}], {a:2,c:10}) //1


Se suggerisci metodi da librerie aggiuntive, dovresti menzionare la loro provenienza.
Craicerjack,

@Steve Bennett nice use.of della libreria ora è in lodash
zabusa

2

Utilizzando il findIndexmetodo ES6 , senza lodash o altre librerie, è possibile scrivere:

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

Ciò confronterà le proprietà immediate dell'oggetto, ma non le ricorrerà nelle proprietà.

Se la tua implementazione non fornisce findIndexancora (la maggior parte no), puoi aggiungere un polyfill leggero che supporti questa ricerca:

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];
    });
  });
}

(dalla mia risposta su questo dupe )


2

In Array.prototype.findIndex()pratica puoi usare una funzione nativa e conveniente :

Il metodo findIndex () restituisce un indice nella matrice, se un elemento nella matrice soddisfa la funzione di test fornita. Altrimenti viene restituito -1.

Solo una nota non è supportato su Internet Explorer, Opera e Safari, ma è possibile utilizzare un Polyfill fornito nel collegamento seguente.

Maggiori informazioni:

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);


2

Furthor di @Monika Garg risposta , è possibile utilizzare findIndex()(C'è un polyfill per i browser unsupprted).

Ho visto che la gente ha votato in negativo questa risposta e spero che lo abbiano fatto a causa della sintassi sbagliata, perché secondo me questo è il modo più elegante.

Il metodo findIndex () restituisce un indice nella matrice, se un elemento nella matrice soddisfa la funzione di test fornita. Altrimenti viene restituito -1.

Per esempio:

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);


Il metodo sembra elegante, ma nessun supporto IE secondo MDN? developer.mozilla.org/en/docs/Web/JavaScript/Reference/…
Jaakko Karhu,

Prova a usare il polyfill (link nella risposta).
Mosh Feu,

1

Questo è il modo di trovare l'indice dell'oggetto nell'array

    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;
        }
    }

0

Funziona senza codice personalizzato

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

Nota: questo non fornisce l'indice effettivo, ma indica solo se l'oggetto esiste nella struttura dati corrente


1
Questo non è valido in quanto non consente di ottenere alcun indice
Antonio Laguna,

0

Puoi semplicemente usare

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);

Nessun carattere di sottolineatura, no per, solo una riduzione.


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"));

Usa l'array un metodo.
user3235365,


-2

Preferirò usare il findIndex()metodo:

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

index ti darà il numero indice.


1
Risposta, ortografia e rientro del codice e :) sono sbagliate?
Sachin Verma,

1
findIndex non è in alcuna implementazione standard javascript. C'è una proposta imminente (ecma 6) per tale metodo, ma la sua firma non è così. Per favore, chiarisci cosa vuoi dire (forse il nome del metodo), dai la dichiarazione del metodo findIndex o dai un nome alla libreria che stai usando.
Sebastien F.
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.