método indexOf em uma matriz de objetos?


514

Qual é o melhor método para obter o índice de uma matriz que contém objetos?

Imagine este cenário:

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

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

Agora eu gostaria de ter o indexOfobjeto qual hellopropriedade é 'stevie'qual, neste exemplo, seria 1.

Sou bastante iniciante em JavaScript e não sei se existe um método simples ou se devo criar minha própria função para fazer isso.


1
Deseja mesclar os dois objetos helloe qaz?
Armin

Não, eu não. Eu quero ter uma lista de objetos em uma matriz.
Antonio Laguna

Ah ok! Você deseja saber a posição de todo o objeto na matriz, que possui uma propriedade definida.
Armin

10
Eu encontrei uma função muito simples de resolver este problema exato com esta resposta 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 é a resposta melhor que a aceita (se o ES6 funcionar para você) - veja o exemplo completo abaixo
yar1

Respostas:


1045

Eu acho que você pode resolvê-lo em uma linha usando a função de mapa :

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

58
Honestamente, essa deve ser a resposta aceita. Atualmente, a maioria dos navegadores suportaArray.prototype.map()
AlbertEngelB

10
Não é suportado pelo IE8, mas, se isso não for um problema, esta é a melhor solução.
Antonio Laguna

57
Hum ... não vale a pena notar que Array.prototype.map () cria uma nova matriz contendo os itens mapeados? Então, se você tem uma matriz com 1000 elementos, criou outra matriz com 1000 elementos primeiro e depois a pesquisa? Acho que vale a pena ver o desempenho desse método versus um loop for simples. Especialmente quando você está executando em uma plataforma móvel com recursos limitados.
Doug

7
@ Doug Embora seu argumento sobre desempenho seja realmente correto, quem em sã consciência substituiria uma linha de código por sete por um aplicativo que é quase por definição IO / Network vinculado até que eles apresentassem gargalos?
Jared Smith

6
Tecnicamente um arquivo JS minified também é uma linha; D
greaterKing

371

Array.prototype.findIndex é suportado em todos os navegadores que não sejam o IE (sem borda). Mas o polyfill fornecido é bom.

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

A solução com o mapa está bem. Mas você está repetindo toda a matriz a cada pesquisa. Esse é apenas o pior caso para findIndex, que para de iterar quando uma correspondência é encontrada.


Não existe realmente uma maneira concisa (quando os desenvolvedores precisavam se preocupar com o IE8) , mas aqui está uma solução comum:

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

ou como uma função:

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

Apenas algumas notas:

  1. Não use para ... em loops em matrizes
  2. Certifique-se de sair do circuito ou retornar à função depois de encontrar sua "agulha"
  3. Cuidado com a igualdade de objetos

Por exemplo,

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

a função tem o errado searchterm comparation como deveria ser searchTerm :)
Antonio Laguna

ocorreram várias ocorrências #
Joe Joe

3
@SteveBennett, é uma versão com desempenho otimizado; o comprimento da matriz deve ser determinado apenas uma vez (quando as variáveis ​​do loop for são inicializadas). No seu caso, o comprimento é verificado a cada iteração novamente. Consulte também stackoverflow.com/questions/5349425/… e stackoverflow.com/questions/8452317/… No entanto, se o desempenho não for alto, isso realmente não importa.
Loot:

1
Boa resposta, mas fiz alguns testes de desempenho (consulte jsperf.com/find-index-of-object-in-array-by-contents ) e constatei que a resposta baseada em função mencionada aqui parece ser a segunda resposta com melhor desempenho. A única coisa que tem mais desempenho acaba sendo colocá-lo em um protótipo, em vez de apenas em uma função, como mencionado na minha resposta .
Uniphonic

123

No ES2015, isso é bem fácil:

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

ou, provavelmente com melhor desempenho para matrizes maiores:

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

2
Boa abordagem para usar ES6
kag

Fiquei surpreso que nenhum desses métodos seja tão eficiente quanto o loop for prototipado, como mencionado na minha resposta. Mesmo que o suporte ao navegador do método findIndex seja um pouco ruim, parece que ele faria a mesma coisa, mas ainda terá menos desempenho? Veja o link na minha resposta para referências.
Uniphonic

É bom saber se o desempenho é importante para a tarefa em questão. Na minha experiência, é muito raramente, mas sim.
Steve Bennett

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

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

}, -1 );

17

Eu gosto da resposta de Pablo, mas as matrizes # indexOf e Array # map não funcionam em todos os navegadores. O sublinhado usará o código nativo se estiver disponível, mas também possui fallbacks. Além disso, ele tem o método de arrancar para fazer exatamente o que o método de mapa anônimo de Pablo faz.

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

1
Array.prototype.map () é compatível com o IE9 + e você pode usar um Polyfill para IE8, 7, 6: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Johann Echavarria

1
Você pode usar um polyfill. . . ou você pode usar sublinhado ou lodash, que são basicamente polyfills que têm vários outros itens em anexo . Qual é a objeção com o sublinhado? Tamanho?
Tandrewnichols

Eu realmente gosto de sublinhado, sua resposta também é inteligente, mas a resposta do IMHO Pablo é a mais limpa.
Johann Echavarria

Uau, eu nunca pensei em usar encadeamento assim. Eu realmente gosto de como torna a pesquisa fluente.
Dylan Pierce

chainé supérfluo aqui. _.pluck(myArray, 'hello').indexOf('stevie')
21818 Steve Bennett

14

Ou protótipo:

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
Muito conveniente! Embora eu escolha prototype.indexOfObject para não interferir com o método Array.indexOf existente. 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
Eu o envolvia em um fechamento auto-executável, com o antigo sendo armazenado de antemão, com a primeira linha da função de substituição sendo algo parecido com if (typeof property === 'string' || typeof property === 'number' || typeof property === 'boolean') return oldIndexOf(property, value);. Isso ocorre porque esses são os poucos tipos que são imutáveis. Eu também destacaria um terceiro argumento para ativar o fallback do método nativo, se necessário.
Isiah Meadows

8

Breve

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

Casos de 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

Fiz alguns testes de desempenho de várias respostas aqui, que qualquer pessoa pode executá-las:

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

Com base nos meus testes iniciais no Chrome, o método a seguir (usando um loop for configurado dentro de um protótipo) é o mais rápido:

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

Este código é uma versão ligeiramente modificada da resposta de Nathan Zaetta.

Nos benchmarks de desempenho, tentei fazer isso com o destino no meio (índice 500) e no final (índice 999) de uma matriz de 1000 objetos, e mesmo se eu colocasse a meta como o último item da matriz (significando que ele precisa percorrer todos os itens da matriz antes de ser encontrado) ainda acaba sendo o mais rápido.

Essa solução também tem o benefício de ser uma das mais concisas para execução repetida, já que somente a última linha precisa ser repetida:

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

2
Eu estava prestes a postar uma resposta a esta pergunta usando um violino com meus próprios testes, mas graças à sua resposta, não preciso mais. Eu só quero confirmar seus testes - cheguei ao mesmo resultado, mas usando um whileloop em vez de um fore performance.now(). Eu desejo que esta resposta foi upvoted mais e que eu vi isso antes, teria me salvou algum tempo ...
Yin Cognyto


5

Embora, a maioria das outras respostas aqui sejam válidas. Às vezes, é melhor criar uma função simples e curta perto de onde você a usará.

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

Depende do que é importante para você. Pode salvar linhas de código e ser muito inteligente usar uma linha, ou colocar uma solução genérica em algum lugar que cubra vários casos extremos. Mas às vezes é melhor apenas dizer: "aqui eu fiz assim" em vez de deixar que desenvolvedores futuros tenham um trabalho extra de engenharia reversa. Especialmente se você se considera "um novato" como em sua pergunta.


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

Cuidado com o [0].

É apropriado usar reducecomo na Antonio Lagunaresposta.

Desculpas pela brevidade ...


4

Se o seu objeto for o mesmo que você está usando na matriz, você poderá obter o índice do objeto da mesma maneira que se fosse uma string.

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

Esta é a resposta que eu estava procurando, pois não estava claro para mim se IndexOf correspondia por valor ou o quê. Agora eu sei que posso usar IndexOf para encontrar meu objeto e não me preocupo se houver outros objetos com as mesmas propriedades.
MDave


3

simples:

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

3

Se você está interessado apenas em encontrar a posição, consulte a resposta de @ Pablo .

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

No entanto, se você está ansioso para encontrar o elemento (ou seja, se você estava pensando em fazer algo assim myArray[pos]), existe uma maneira de uma linha mais eficiente de fazê-lo, usando filter.

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

Veja resultados de desempenho (~ + 42% ops / s): http://jsbench.github.io/#7fa01f89a5dc5cc3bee79abfde80cdb3


2

Veja este exemplo: http://jsfiddle.net/89C54/

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

Começa a contar com zero.


2

Eu fiz uma função genérica para verificar o código abaixo e funciona para qualquer objeto

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

O primeiro alerta retornará -1 (significa que a correspondência não foi encontrada) e o segundo alerta retornará 0 (significa que a correspondência foi encontrada).


2

Use _.findIndexda biblioteca underscore.js

Aqui está o exemplo _.findIndex([{a:1},{a: 2,c:10},{a: 3}], {a:2,c:10}) //1


Ao sugerir métodos de bibliotecas adicionais, você deve mencionar de onde eles vêm.
Craicerjack

@ Steve Bennett agradável use.of a biblioteca agora o seu lodash em
zabusa

2

Usando o findIndexmétodo ES6 , sem lodash ou qualquer outra biblioteca, você pode escrever:

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

Isso irá comparar as propriedades imediatas do objeto, mas não se repetirá nas propriedades.

Se sua implementação ainda não fornecer findIndex(a maioria não fornece ), você poderá adicionar um polyfill leve que ofereça suporte a esta pesquisa:

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

(da minha resposta sobre esse idiota )


2

Você pode usar uma função nativa e conveniente Array.prototype.findIndex()basicamente:

O método findIndex () retorna um índice na matriz, se um elemento na matriz atender à função de teste fornecida. Caso contrário, -1 é retornado.

Apenas uma observação: ele não é suportado no Internet Explorer, Opera e Safari, mas você pode usar um Polyfill fornecido no link abaixo.

Mais Informações:

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

Além da resposta do @Monika Garg , você pode usar findIndex()(existe um polyfill para navegadores não suportados).

Vi que as pessoas votaram negativamente nesta resposta e espero que tenham feito isso devido à sintaxe errada, porque, na minha opinião, essa é a maneira mais elegante.

O método findIndex () retorna um índice na matriz, se um elemento na matriz atender à função de teste fornecida. Caso contrário, -1 é retornado.

Por exemplo:

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


O método parece elegante, mas não há suporte do IE de acordo com o MDN? developer.mozilla.org/en/docs/Web/JavaScript/Reference/…
Jaakko Karhu

Tente usar o polyfill (link na resposta).
Mosh Feu

1

Esta é a maneira de encontrar o índice do objeto na matriz

    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

Isso funciona sem código personalizado

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: isso não fornece o índice real, apenas informa se o seu objeto existe na estrutura de dados atual


1
Isso não é válido, pois não permite obter nenhum índice.
Antonio Laguna

0

Você pode simplesmente usar

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

Sem sublinhado, não por, apenas uma redução.


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

Use Array algum método.
user3235365


-2

Eu vou preferir usar o findIndex()método:

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

index lhe dará o número do índice.


1
Resposta, ortografia e recuo do código e :) estão errados?
Sachin Verma

1
O findIndex não está em nenhuma implementação padrão do javascript. Existe uma proposta futura (ecma 6) para esse método, mas sua assinatura não é assim. Por favor, esclareça o que você quer dizer (talvez o nome do método), forneça a declaração do método findIndex ou nomeie a biblioteca que você está usando.
Sebastien F.
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.