En una matriz de objetos, la forma más rápida de encontrar el índice de un objeto cuyos atributos coinciden con una búsqueda

135

He estado navegando un poco tratando de encontrar una manera eficiente de hacer esto, pero no he llegado a ninguna parte. Tengo una serie de objetos que se ve así:

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

Lo que quiero hacer es encontrar los ÍNDICES de los objetos donde id es igual a, por ejemplo, uno de 0,1,2,3 o 4. Supongo que podría hacer algo como:

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

Si bien esto funcionaría, parece ser bastante costoso y lento (sin mencionar que es feo), especialmente si array.length podría ser grande. ¿Alguna idea sobre cómo arreglar esto un poco? Pensé en usar array.indexOf de alguna manera, pero no veo cómo forzar la sintaxis. Esta

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

por ejemplo, devuelve indefinido, como probablemente debería. ¡Gracias por adelantado!

Petrov
fuente
1
Si tiene una matriz antigua simple, todo lo que puede hacer es iterar. Eso es lo que son las matrices, un montón de objetos ordenados por índice de matriz.
Dave Newton
2
Acabo de encontrar esta publicación hoy, para todos los recién llegados hay un nuevo método de matriz Array.prototype.findIndex()en ECMAScript 2015. La respuesta aceptada fue impresionante.
Conrad Lo
Soy fanático de la sintaxis de ES6 (use polyfills, si se necesita soporte en navegadores heredados). ES7 + ES8 serán futuros
Fr0zenFyr

Respuestas:

391

Tal vez le gustaría utilizar funciones de orden superior como "mapa". Suponiendo que desea buscar por atributo 'campo':

var elementPos = array.map(function(x) {return x.id; }).indexOf(idYourAreLookingFor);
var objectFound = array[elementPos];
Pablo Francisco Pérez Hidalgo
fuente
9
Esta respuesta es genial porque en realidad responde la pregunta al proporcionar el índice :)
contrarrestando el
3
@ZeroAbsolute Su función aplicada (pasada al mapa) puede devolver una cadena hash que debería proporcionar una clave única para cada combinación posible dada por sus criterios. Por ejemplo: function hashf(el) { return String(el.id) + "_" + String(el.name); }. Esto es solo una pista: elementPos = array.map(hashf(x)).indexOf(hash({id:3, name:'Pablo'}));obviamente, la función hash que proporciono no es válida para todos los casos, ya que '_'podría formar parte de sus valores, pero es solo un ejemplo rápido de que puede descubrir diferentes métodos hash.
Pablo Francisco Pérez Hidalgo
1
¿Qué devuelve esto si no se encuentra? Asumo -1, solo curiosidad. Voy a experimentar
Nathan C. Tresch
1
@ NathanC.Tresch Devuelve -1 porque ese es el indexOfvalor de retorno cuando no puede localizar un valor dado.
Pablo Francisco Pérez Hidalgo
2
Hola a todos, en lugar de usar dos métodos map, indexOf, puede usar solo uno llamado findIndex....... Ej:[{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

La forma más simple y fácil de encontrar el índice de elementos en la matriz.

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

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

Umair Ahmed
fuente
44
Creo que esta es la solución más elegante. Para aquellos preocupados por la compatibilidad con versiones anteriores, puede encontrar el polyfill findIndexen developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
mrogers
2
Recibo una advertencia en mi herramienta de pelusa ES6 de que el obj.id == 3operador utilizado aquí puede causar una conversión de tipo inesperada, así que use el obj.id === 3operador, que prueba el mismo valor y tipo.
thclark
1
Esta respuesta es al menos 3.5 veces más rápida que la respuesta aceptada anteriormente. Usarlo var elementPos = array.map(function(x) {return x.id; }).indexOf(idYourAreLookingFor);tomó 0.03500000002532033 milisegundos Usarlo [{id:1},{id:2},{id:3},{id:4}].findIndex(obj => obj.id == 3)tomó 0.00999999747378752 milisegundos.
Ovidio Reyna
1
ESTA RESPUESTA es la más EFICIENTE ya que no itera toda la matriz. La respuesta seleccionada asignará la matriz completa y luego findIndex, que seguramente iterará a través de toda la matriz una vez
Karun
26

El nuevo método de matriz .filter () funcionaría bien para esto:

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

jQuery también puede hacer esto con .grep ()

editar: vale la pena mencionar que estas dos funciones simplemente iteran bajo el capó, no habrá una diferencia de rendimiento notable entre ellas y rodar su propia función de filtro, pero ¿por qué reinventar la rueda?

jbabey
fuente
+1, siempre me olvido de las funciones integradas como esta en los objetos.
Tejs
59
Esto no devuelve un índice.
Adam Grant
Esto no responde a esta pregunta específica, ¡pero ayúdame mucho! ¡Gracias!
rochasdv el
Esto no devuelve el índice.
Rico
10

Si se preocupan por el rendimiento, no vayas con hallazgo o filtro o asignar o cualquiera de los métodos anteriormente discutidos

Aquí hay un ejemplo que demuestra el método más rápido. AQUÍ está el enlace a la prueba real

Bloque de configuración

var items = []

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

var find = 523

Método más rápido

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

Métodos más lentos

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

Método más lento

items.map(item => item.id).indexOf(find);
PirateApp
fuente
2
¡Gracias por proporcionar esta comparación! Lo que es súper interesante es la cantidad de rendimiento que varía, incluido el método que varía más rápidamente dependiendo del motor de navegador / JavaScript utilizado para ejecutarlos.
Iain Collins el
1
Creo que esto debería ser marcado como una respuesta. Esto muestra la forma más rápida y más lenta.
Analgésico
En su punto de referencia, el bloque 2 (usando findIndex) en realidad es más rápido para mí (en Microsoft Edge Chromium 83.0.474.0)
rezadru
El bloque 2 ahora también es más rápido en 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

el resultado es una lista de búsqueda para la identificación. con la identificación dada obtenemos el índice del registro.

Nina Scholz
fuente
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
fuente
6

Como no hay respuesta usando una matriz regular 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
fuente
3

Una nueva forma de usar ES6

let picked_element = array.filter(element => element.id === 0);
Silve2611
fuente
picked_elementes una matriz en este caso ...
Heretic Monkey
3

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

Esto debería obtener el índice del elemento en la matriz con id === your-id

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

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

console.log(index);

PulpDood
fuente
2

Me parece que podría crear un iterador simple con una devolución de llamada para probar. Al igual que:

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

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

    return matchingIndices;
}

Entonces podrías invocar así:

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
fuente
2

Adaptando la respuesta de Tejs para mongoDB y Robomongo cambié

matchingIndices.push(j);

a

matchingIndices.push(NumberInt(j+1));
usuario2584621
fuente
2

Usando la mapfunción ES6 :

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

Para resumir toda la gran respuesta anterior y adicional de mi respuesta con respecto a encontrar todos los índices, se produjo en algunos de los comentarios.

  1. Para devolver el índice de la primera aparición.

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. Para devolver la matriz de índice de todas las ocurrencias, use reducir.

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
fuente
0

Como todavía no puedo comentar, quiero mostrar la solución que utilicé según el método publicado por Umair Ahmed, pero cuando desea buscar una clave en lugar de un valor:

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

Entiendo que no responde a la pregunta ampliada, pero el título no especifica lo que se quería de cada objeto, por lo que quiero compartir esto humildemente para ahorrar dolores de cabeza a los demás en el futuro, aunque empiezo, puede que no sea el La solución más rápida.

Xander N
fuente
0

Creé una pequeña utilidad llamada super-array donde puedes acceder a los elementos de una matriz mediante un identificador único con complejidad O (1). Ejemplo:

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
fuente
Es posible que desee leer ¿Cómo ofrecer bibliotecas personales de código abierto? antes de publicar esto en todas partes.
Martijn Pieters
@MartijnPieters Lo publiqué solo en unas pocas preguntas relevantes y el proyecto está libre de MIT, ¿cuál es el problema? Tal vez podrías ser un poco más tolerante.
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;
}

devolverá el índice 1 (solo funciona en ES 2016)

extremo
fuente
0

Me gusta este método porque es fácil de comparar con cualquier valor en el objeto, sin importar qué tan profundo esté anidado.

 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
fuente