In una matrice di oggetti, il modo più veloce per trovare l'indice di un oggetto i cui attributi corrispondono a una ricerca


135

Ho navigato un po 'cercando di trovare un modo efficiente per farlo, ma non sono arrivato da nessuna parte. Ho una serie di oggetti che assomiglia a questo:

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

Quello che voglio fare è trovare gli INDICI degli oggetti in cui id è uguale, ad esempio, a 0,1,2,3 o 4. Suppongo che potrei semplicemente fare qualcosa del tipo:

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

Mentre questo funzionerebbe, sembra essere piuttosto costoso e lento (per non parlare dei brutti), specialmente se array.length potrebbe essere grande. Qualche idea su come abbellirlo un po '? Ho pensato di usare array.index In qualche modo, ma non vedo come forzare la sintassi. Questo

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

ad esempio, restituisce undefined, come probabilmente dovrebbe. Grazie in anticipo!


1
Se hai un vecchio array semplice, tutto ciò che puoi fare è iterare. Ecco cosa sono le matrici, un gruppo di oggetti ordinati per indice di matrice.
Dave Newton,

2
Trovate questo post oggi, per tutti i ritardatari c'è un nuovo metodo di array Array.prototype.findIndex()in ECMAScript 2015. La risposta accettata è stata comunque fantastica.
Conrad Lo,

Sono un fan della sintassi ES6 (utilizzare i polyfill, se è necessario il supporto sui browser legacy). ES7 + ES8 saranno futuri
Fr0zenFyr

Risposte:


391

Forse ti piacerebbe usare funzioni di ordine superiore come "map". Supponendo di voler cercare per attributo 'field':

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

9
Questa risposta è fantastica perché in realtà risponde alla domanda fornendo l'indice :)
contrasto con il

3
@ZeroAbsolute La tua funzione applicata (passata alla mappa) può restituire una stringa hash che dovrebbe fornire una chiave univoca per ogni possibile combinazione data dai tuoi criteri. Ad esempio: function hashf(el) { return String(el.id) + "_" + String(el.name); }. Questo è solo un suggerimento: elementPos = array.map(hashf(x)).indexOf(hash({id:3, name:'Pablo'}));ovviamente, la funzione di hash che fornisco non è valida per tutti i casi poiché '_'potrebbe far parte dei tuoi valori ma è solo un rapido esempio che puoi capire diversi metodi di hash.
Pablo Francisco Pérez Hidalgo,

1
Cosa ritorna se non viene trovato? Presumo -1, solo curioso. Sperimenterò.
Nathan C. Tresch,

1
@ NathanC.Tresch Restituisce -1 perché è un indexOfvalore restituito quando non è in grado di individuare un determinato valore.
Pablo Francisco Pérez Hidalgo,

2
Salve a tutti invece di usare due metodi map, indexOf, potete usarne solo uno chiamato findIndex....... Es:[{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

Il modo più semplice e facile per trovare l'indice degli elementi nell'array.

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

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


4
Credo che questa sia la soluzione più elegante. Per chi è preoccupato per la compatibilità con le versioni precedenti, è possibile trovare il polyfill findIndexsu developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
mrogers,

2
Ricevo un avviso nel mio strumento per lanugine ES6 che l' obj.id == 3operatore utilizzato qui può causare una conversione di tipo imprevista, quindi utilizzare obj.id === 3invece l' operatore, che verifica lo stesso valore e tipo.
thclark,

1
Questa risposta è almeno 3,5 volte più veloce della risposta accettata sopra. L'utilizzo var elementPos = array.map(function(x) {return x.id; }).indexOf(idYourAreLookingFor);ha richiesto 0,03500000002532033 millisecondi L'utilizzo [{id:1},{id:2},{id:3},{id:4}].findIndex(obj => obj.id == 3)ha richiesto 0,00999999747378752 millisecondi.
Ovidio Reyna,

1
QUESTA RISPOSTA è la più EFFICACE in quanto non itera l'intero array. La risposta selezionata mapperà l'array completo e poi troverà Index che è destinato a scorrere una volta l'intero array
Karun,

26

Il nuovo metodo Array .filter () funzionerebbe bene per questo:

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

jQuery può anche farlo con .grep ()

modifica: vale la pena ricordare che entrambe queste funzioni eseguono l'iterazione sotto il cofano, non ci sarà una notevole differenza di prestazioni tra loro e il rotolamento della propria funzione di filtro, ma perché reinventare la ruota.


+1, mi dimentico sempre di funzioni integrate come questa sugli oggetti.
Tejs,

59
Questo non restituisce un indice.
Adam Grant,

Questo non risponde a questa domanda specifica, ma mi aiuta molto! Grazie!
rochasdv,

Questo non restituisce l'indice.
Rich

10

Se ti preoccupi delle prestazioni, non andare con find o filter o map o con uno dei metodi sopra discussi

Ecco un esempio che dimostra il metodo più veloce. QUI è il link al test effettivo

Blocco di installazione

var items = []

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

var find = 523

Metodo più veloce

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

Metodi più lenti

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

Metodo più lento

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

2
Grazie per aver fornito questo confronto! Ciò che è super interessante è la quantità di prestazioni varia, incluso il metodo che varia più rapidamente a seconda del browser / motore JavaScript utilizzato per eseguirle.
Iain Collins,

1
Penso che questo dovrebbe essere contrassegnato come una risposta. Questo mostra il modo più veloce e quelli più lenti.
Painkiller,

Nel tuo benchmark, il blocco 2 (usando findIndex) in realtà è più veloce per me (su Microsoft Edge Chromium 83.0.474.0)
rezadru

Il blocco 2 è ora più veloce anche su Chrome
cody mikol il

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

il risultato è un elenco di ricerca per l'id. con l'id specificato otteniamo l'indice del record.


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

Poiché non vi è alcuna risposta utilizzando l'array normale 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

Un nuovo modo di usare ES6

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

picked_elementè un array in questo caso ...
Heretic Monkey il

3

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

Questo dovrebbe farti ottenere l'indice dell'articolo nell'array con id === your-id

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

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

console.log(index);


2

Mi sembra che potresti creare un semplice iteratore con un callback per il test. Così:

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

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

    return matchingIndices;
}

Quindi potresti invocare in questo modo:

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

Adattando la risposta di Tejs per mongoDB e Robomongo ho cambiato

matchingIndices.push(j);

per

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

2

Utilizzando la mapfunzione ES6 :

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

2

Per riassumere tutta la grande risposta sopra e ulteriore della mia risposta per quanto riguarda trova tutti gli indici occorsi da alcuni dei commenti.

  1. Per restituire l'indice della prima occorrenza.

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. Per restituire la matrice dell'indice di tutte le occorrenze, usando il comando riduci.

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

Poiché non posso ancora commentare, voglio mostrare la soluzione che ho usato in base al metodo pubblicato da Umair Ahmed, ma quando si desidera cercare una chiave anziché un valore:

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

Capisco che non risponde alla domanda espansa, ma il titolo non specifica ciò che era desiderato da ciascun oggetto, quindi voglio condividerlo umilmente per salvare il mal di testa agli altri in futuro, mentre riesco a non avviarlo potrebbe non essere il soluzione più veloce.


0

Ho creato una piccola utility chiamata super-array in cui è possibile accedere agli elementi in un array tramite un identificatore univoco con complessità O (1). Esempio:

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

Potresti voler leggere Come offrire librerie open source personali? prima di pubblicarlo ovunque.
Martijn Pieters

@MartijnPieters L'ho pubblicato solo per alcune domande pertinenti e il progetto è privo di MIT, quindi qual è il problema? Forse potresti essere un po 'più tollerante.
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;
}

restituirà l'indice 1 (funziona solo in ES 2016)


0

Mi piace questo metodo perché è facile paragonarlo a qualsiasi valore nell'oggetto, non importa quanto profondo sia nidificato.

 while(i<myArray.length && myArray[i].data.value!==value){
  i++; 
}
// i now hows the index value for the match. 
 console.log("Index ->",i );
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.