Como obter o índice do objeto por sua propriedade em JavaScript?

241

Por exemplo, eu tenho:

var Data = [
  { id_list: 1, name: 'Nick', token: '312312' },
  { id_list: 2, name: 'John', token: '123123' },
]

Então, quero classificar / reverter esse objeto name, por exemplo. E então eu quero obter algo parecido com isto:

var Data = [
  { id_list: 2, name: 'John', token: '123123' },
  { id_list: 1, name: 'Nick', token: '312312' },
]

E agora eu quero saber o índice do objeto com propriedade name='John'para obter o valor do token de propriedade.

Como resolvo o problema?

rsboarder
fonte
Por que você deseja classificar a lista antes de procurar a propriedade?
JJJ
Objetos JavaScript são {Key:Value}, eu consertei para você.
Rocket Hazmat
Se você examinar as respostas, parece que há algum Dataobjeto nativo . É apenas um nome de variável em maiúscula, o que é contra a convenção. Se alguém mais se incomodar com isso, farei edições na pergunta e respostas para corrigir essa nomeação.
Roy Prins
Pergunta relacionada: stackoverflow.com/questions/10557486/…
Anton Tarasenko

Respostas:

170

Como as outras respostas sugerem, fazer um loop pela matriz é provavelmente a melhor maneira. Mas eu colocaria isso em sua própria função e o tornaria um pouco mais abstrato:

function findWithAttr(array, attr, value) {
    for(var i = 0; i < array.length; i += 1) {
        if(array[i][attr] === value) {
            return i;
        }
    }
    return -1;
}

var Data = [
    {id_list: 2, name: 'John', token: '123123'},
    {id_list: 1, name: 'Nick', token: '312312'}
];

Com isso, você não apenas pode encontrar qual deles contém 'John', mas também qual deles contém o token '312312':

findWithAttr(Data, 'name', 'John'); // returns 0
findWithAttr(Data, 'token', '312312'); // returns 1
findWithAttr(Data, 'id_list', '10'); // returns -1

EDIT: Função atualizada para retornar -1 quando não encontrado, para que siga a mesma construção que Array.prototype.indexOf ()

Chris Pickett
fonte
1
Resposta adicionada estendendo isso para cobrir pesquisas mais profundas que um nível de atributo. Obrigado por este Chris - realmente ajudou: D
Ed Shee
3
Para aqueles que terão problemas com esta solução, lembre-se de que o sinal de igual === verificará não apenas o valor, mas também o tipo de dados. portanto, comparando data, números e valores vazios podem retornar false se o valor real for uma sequência. Você pode usar == sinal de igual se o tipo de dados não for essencial para você.
David Addoteye
NÃO FAÇA ISSO. Aprenda a usar funções de ordem superior. Essa é a maneira antiga de fazer as coisas.
Levou
327

Uma vez que a parte de classificação já está respondida. Vou apenas propor outra maneira elegante de obter o indexOf de uma propriedade em sua matriz

Seu exemplo é:

var Data = [
    {id_list:1, name:'Nick',token:'312312'},
    {id_list:2,name:'John',token:'123123'}
]

Você pode fazer:

var index = Data.map(function(e) { return e.name; }).indexOf('Nick');

Array.prototype.mapnão está disponível no IE7 ou IE8. Compatibilidade com ES5

E aqui está o ES6 e a sintaxe da seta, que é ainda mais simples:

const index = Data.map(e => e.name).indexOf('Nick');
Attanasio alemão
fonte
4
Eu tenho que repetir toda a matriz, não importa o quê. O primeiro método não precisa.
German Attanasio
1
Simples, mas ter em conta o desempenho ao utilizar grandes conjuntos de dados
gavioto
1
Realmente ótima resposta me ajudou com este problema!
NiallMitch14
1
Esta solução é adorável. Que você viva até os mil anos, senhor.
deepelement 5/01/19
3
Fazer um mapa e, em seguida, indexOf na versão ES6 significa que eles percorrerão a matriz duas vezes. Em vez disso você deve usar o método FindIndex mostrado na minha resposta
silverlight513
209

Se você está bem em usar o ES6. As matrizes agora têm a função findIndex . O que significa que você pode fazer algo assim:

const index = Data.findIndex(item => item.name === 'John');
silverlight513
fonte
6
sem dúvida a solução mais direta e elegante, eu faria um pequeno não que findIndex seja ganancioso - no meu caso, foi perfeito - mas os usuários devem estar cientes de que se você tiver objetos com valores duplicados (ou seja, dois objetos com name: John), isso retornará apenas a primeira partida
GrayedFox 21/10
Impressionante!! Obrigado.
moreirapontocom
@GrayedFox você não quis dizer NÃO ganancioso? Ganancioso significa mais resultados e o findIndex retorna apenas a primeira correspondência.
Jos
1
Desculpe @ Jos, mas acho que você não entende muito bem o que um algoritmo de pesquisa ganancioso faz . Um algoritmo ganancioso considera apenas as informações que tem em mãos, que são computacionalmente eficientes para, por exemplo, procurar um item em uma lista quando você precisa de apenas um resultado. O método findIndex é ganancioso, porque retorna apenas a primeira correspondência. Se não fosse ganancioso, continuaria pesquisando. Você está pensando do termo "gananciosos", como é usado no dia-a-dia Inglês, mas no contexto de programação (ou seja SO), é o oposto do que você pensa;)
GrayedFox
Obrigado por explicar @GrayedFox. Minha referência a ganancioso também foi de programação, mas relacionada a expressões regulares, onde ganancioso tem mais caracteres do que não gananciosos! :-)
Jos
28
var index = Data.findIndex(item => item.name == "John")

Qual é uma versão simplificada de:

var index = Data.findIndex(function(item){ return item.name == "John"})

Em mozilla.org:

O método findIndex () retorna o índice do primeiro elemento na matriz que satisfaz a função de teste fornecida. Caso contrário, -1 será retornado.

edank
fonte
2
Use "===" para comparação de cadeias, é a única coisa que falta nesta resposta.
Bruno Tavares
@BrunoTavares, qual cenário você acha que seria diferente nesse caso?
edank
1
@edank Se o OP verificar o tokencampo em vez do namecampo, pode haver problemas, como Data[0].token == 312312avalia para true, mas Data[0].token === 321321avalia para false.
kem
@edank, dê uma olhada nesta resposta stackoverflow.com/questions/359494/…
Bruno Tavares
23

Se você estiver tendo problemas com o IE, poderá usar a função map () que é suportada a partir do 9.0:

var index = Data.map(item => item.name).indexOf("Nick");
Alain T.
fonte
1
A resposta abaixo fornece mais informações e foi publicada primeiro.
Alexander Alexander
Esta é definitivamente a melhor solução para isso.
Bebolicious
4

A única maneira conhecida por mim é percorrer toda a matriz:

var index=-1;
for(var i=0;i<Data.length;i++)
  if(Data[i].name==="John"){index=i;break;}

ou não diferencia maiúsculas de minúsculas:

var index=-1;
for(var i=0;i<Data.length;i++)
  if(Data[i].name.toLowerCase()==="john"){index=i;break;}

No resultado, o índice variável contém o índice do objeto ou -1 se não for encontrado.

Andrew D.
fonte
2

uma maneira prototípica

(function(){
  if (!Array.prototype.indexOfPropertyValue){
    Array.prototype.indexOfPropertyValue = function(prop,value){
      for (var index = 0; index < this.length; index++){
        if (this[index][prop]){
          if (this[index][prop] == value){
            return index;
          }
        }
      }
      return -1;
    }
  }
 })();
 // usage:
 var Data = [
 {id_list:1, name:'Nick',token:'312312'},{id_list:2,name:'John',token:'123123'}];
 Data.indexOfPropertyValue('name','John'); // returns 1 (index of array);
 Data.indexOfPropertyValue('name','Invalid name') // returns -1 (no result);
 var indexOfArray = Data.indexOfPropertyValue('name','John');
 Data[indexOfArray] // returns desired object.
Patrik Wallin
fonte
Eu tive que mudar isso um pouco para trabalhar para a minha situação - eu estava procurando o índice de uma propriedade com um valor nulo e a quinta linha estava fazendo com que isso pulasse sobre esse índice.
David Brunow
1

Você pode usar Array.sort usando uma função personalizada como parâmetro para definir seu mecanismo de classificação.

No seu exemplo, daria:

var Data = [
    {id_list:1, name:'Nick',token:'312312'},{id_list:2,name:'John',token:'123123'}
]

Data.sort(function(a, b){
    return a.name < b.name ? -1 : a.name > b.name ? 1 : 0;
});

alert("First name is : " + Data[0].name); // alerts 'John'
alert("Second name is : " + Data[1].name); // alerts 'Nick'

A função de classificação deve retornar -1 se a vier antes de b, 1 se a vir depois de be 0 se ambos forem iguais. Cabe a você definir a lógica correta em sua função de classificação para classificar a matriz.

Editar: perdeu a última parte da sua pergunta em que você deseja conhecer o índice. Você teria que percorrer a matriz para descobrir isso, como outros disseram.


fonte
1

Basta percorrer sua matriz e encontrar a posição:

var i = 0;
for(var item in Data) {
    if(Data[item].name == 'John')
        break;
    i++;
}
alert(i);
Sascha Galley
fonte
Deve ser if(Data[item].name == 'John'), eu consertei para você.
Rocket Hazmat
Isso tem um momento. Se 'não encontrado' variável i contêm índice positivo. E para test 'não encontrado' necessidades para comparar, se eu === Data.length
Andrew D.
O segundo momento é se a matriz contiver propriedades personalizadas não indexadoras. Por exemplo: var Data [1,2,3,4,5]; Dados ["teste"] = 7897; Compare a saída de: para alerta (var i in Data) (Data [i]); e para (var i = 0; i <Data.length; i ++) alerta (Data [i]);
Andrew D.
1

Por que você não usa uma pequena solução alternativa?

Crie uma nova matriz com nomes como índices. depois disso, todas as pesquisas usarão índices. Então, apenas um loop. Depois disso, você não precisa percorrer todos os elementos!

var Data = [
    {id_list:1, name:'Nick',token:'312312'},{id_list:2,name:'John',token:'123123'}
    ]
var searchArr = []
Data.forEach(function(one){
  searchArr[one.name]=one;
})
console.log(searchArr['Nick'])

http://jsbin.com/xibala/1/edit

exemplo ao vivo.


fonte
Isso significa dar uma volta por tudo primeiro ... depois modificar objetos (a menos que você os clone, mas isso é ainda mais caro). E depois disso você volta novamente (pelo menos parcialmente).
18719 Jos Jos
1

Resposta estendida de Chris Pickett, porque no meu caso eu precisava pesquisar mais do que um nível de atributo:

function findWithAttr(array, attr, value) {
  if (attr.indexOf('.') >= 0) {
    var split = attr.split('.');
    var attr1 = split[0];
    var attr2 = split[1];
    for(var i = 0; i < array.length; i += 1) {
      if(array[i][attr1][attr2] === value) {
        return i;
      }
    }
  } else {
    for(var i = 0; i < array.length; i += 1) {
      if(array[i][attr] === value) {
        return i;
      }
    }
  };
};

Você pode passar 'attr1.attr2' para a função

Ed Shee
fonte
1

Que tal isso? :

Data.indexOf(_.find(Data, function(element) {
  return element.name === 'John';
}));

Supondo que você esteja usando lodash ou underscorejs.

Ellone
fonte
1
var fields = {
teste:
{
    Acess:
    {
        Edit: true,
      View: false

      }
 },
teste1:
{
    Acess:
    {
        Edit: false,
      View: false

      }
  }
};

console.log(find(fields,'teste'));
function find(fields,field){
    for(key in fields){
        if(key == field){
        return true;
    }
}
return false;
}

Se você tiver um Objeto com objetos de multiplicação, se quiser saber se algum objeto está incluído no objeto Mestre, basta colocar find (MasterObject, 'Objeto a Pesquisar'), essa função retornará a resposta se existir ou não (TRUE ou FALSE ), Espero que ajude com isso, veja o exemplo no JSFiddle .

Pedro Monteiro Arantes
fonte
Adicione um pouco de explicação com a resposta de como essa resposta ajuda OP na fixação edição atual
ρяσѕρєя K
@ ρяσѕρєяK, Obrigado por sua sugestão, eu já adicionar a explicação :)
Pedro Monteiro Arantes
1
let indexOf = -1;
let theProperty = "value"
let searchFor = "something";

theArray.every(function (element, index) {

    if (element[theProperty] === searchFor) {
        indexOf = index;
        return false;
    }
    return true;
});
Itay Merchav
fonte
1
collection.findIndex(item => item.value === 'smth') !== -1
Eugene Lyzo
fonte
Embora esse trecho de código possa resolver a questão, incluir uma explicação realmente ajuda a melhorar a qualidade da sua postagem. Lembre-se de que você está respondendo à pergunta dos leitores no futuro e essas pessoas podem não saber os motivos da sua sugestão de código. Tente também não sobrecarregar seu código com comentários explicativos, pois isso reduz a legibilidade do código e das explicações!
Adeus StackExchange
1

Se você deseja obter o valor do token de propriedade, também pode tentar isso

    let data=[
      { id_list: 1, name: 'Nick', token: '312312' },
      { id_list: 2, name: 'John', token: '123123' },
    ]

    let resultingToken =  data[_.findKey(data,['name','John'])].token

onde _.findKey é uma função do lodash

Akash Singh
fonte
0

Como alternativa à resposta do alemão Attanasio Ruiz, você pode eliminar o segundo loop usando Array.reduce () em vez de Array.map ();

var Data = [
    { name: 'hypno7oad' }
]
var indexOfTarget = Data.reduce(function (indexOfTarget, element, currentIndex) {
    return (element.name === 'hypno7oad') ? currentIndex : indexOfTarget;
}, -1);
hypno7oad
fonte
-1

Usando sublinhado:

var index = _.indexOf(_.pluck(item , 'name'), 'Nick');
M Faisal Hameed
fonte