Obter o índice do objeto dentro de uma matriz, correspondendo a uma condição

322

Eu tenho uma matriz como esta:

[{prop1:"abc",prop2:"qwe"},{prop1:"bnmb",prop2:"yutu"},{prop1:"zxvz",prop2:"qwrq"},...]

Como posso obter o índice do objeto que corresponde a uma condição, sem iterar por toda a matriz?

Por exemplo, dado prop2=="yutu", eu quero obter o índice 1.

Eu vi, .indexOf()mas acho que é usado para matrizes simples como ["a1","a2",...]. Eu também verifiquei, $.grep()mas isso retorna objetos, não o índice.

amp
fonte

Respostas:

733

A partir de 2016, você deve usar Array.findIndex(um padrão ES2015 / ES6) para isso:

a = [
  {prop1:"abc",prop2:"qwe"},
  {prop1:"bnmb",prop2:"yutu"},
  {prop1:"zxvz",prop2:"qwrq"}];
    
index = a.findIndex(x => x.prop2 ==="yutu");

console.log(index);

É suportado no Google Chrome, Firefox e Edge. Para o Internet Explorer, há um polyfill na página vinculada.

Nota de desempenho

As chamadas de função são caras, portanto, com matrizes realmente grandes, um loop simples terá um desempenho muito melhor que findIndex:

let test = [];

for (let i = 0; i < 1e6; i++)
    test.push({prop: i});


let search = test.length - 1;
let count = 100;

console.time('findIndex/predefined function');
    let fn = obj => obj.prop === search;

    for (let i = 0; i < count; i++)
        test.findIndex(fn);
console.timeEnd('findIndex/predefined function');


console.time('findIndex/dynamic function');
    for (let i = 0; i < count; i++)
        test.findIndex(obj => obj.prop === search);
console.timeEnd('findIndex/dynamic function');


console.time('loop');
    for (let i = 0; i < count; i++) {
        for (let index = 0; index < test.length; index++) {
            if (test[index].prop === search) {
                break;
            }
        }
    }
console.timeEnd('loop');

Como na maioria das otimizações, isso deve ser aplicado com cuidado e somente quando realmente necessário.

georg
fonte
3
Não estou vendo a necessidade de uma matriz temporária aqui. Apenas use o fato de que a função iteradora fecha sobre o contexto e use uma variável. Além disso, a versão não-jQuery não funciona (suponha que seja encontrada no índice 0?). Ambas as soluções fazem mais iterações do que o necessário, o que é menos que o ideal se a matriz for grande (embora as chances de ser tão grande que um ser humano notaria sejam baixas, a menos que as pesquisas estejam acontecendo muito ).
TJ Crowder
@ thg435: Ainda acho que é um pouco de uma máquina Rube Goldberg onde uma simples alavanca faria o truque. :-) Mas ei, funciona!
TJ Crowder
4
Você pode explicar como x => x.prop2=="yutu"funciona o findIndex ()?
Abhay Pai 27/02
5
@AbhayPai: é o mesmo quefunction(x) { return x.prop2=="yutu" }
Georg
6
Gosto da sugestão de usar o polyfill. No entanto, o código escrito ainda falha no IE11, mesmo com o polyfill, devido ao uso de uma função arrow / lambda. Re-escrito como index = a.findIndex(function (x) { return x.prop2 == "yutu" })corrigiu o problema para que, com o código de polyfill, FindIndex trabalhou no IE11
Rick Glos
26

Como posso obter o índice do objeto que corresponde a uma condição (sem iterar ao longo da matriz)?

Você não pode, algo precisa percorrer a matriz (pelo menos uma vez).

Se a condição mudar muito, você terá que percorrer e examinar os objetos nele para ver se eles correspondem à condição. No entanto, em um sistema com recursos do ES5 (ou se você instalar um shim), essa iteração pode ser feita de forma bastante concisa:

var index;
yourArray.some(function(entry, i) {
    if (entry.prop2 == "yutu") {
        index = i;
        return true;
    }
});

Isso usa a nova Array#somefunção (ish) , que percorre as entradas na matriz até que a função que você fornece retorne true. A função que forneci salva o índice da entrada correspondente e retorna truepara interromper a iteração.

Ou, é claro, basta usar um forloop. Suas várias opções de iteração são abordadas nesta outra resposta .

Mas se você sempre usará a mesma propriedade para esta pesquisa e se os valores da propriedade forem únicos, poderá fazer um loop apenas uma vez e criar um objeto para mapeá-los:

var prop2map = {};
yourArray.forEach(function(entry) {
    prop2map[entry.prop2] = entry;
});

(Ou, novamente, você pode usar um forloop ou qualquer outra opção .)

Então, se você precisar encontrar a entrada com prop2 = "yutu", poderá fazer o seguinte:

var entry = prop2map["yutu"];

Eu chamo isso de "indexação cruzada" de matriz. Naturalmente, se você remover ou adicionar entradas (ou alterar seus prop2valores), também precisará atualizar seu objeto de mapeamento.

TJ Crowder
fonte
Obrigada pelo esclarecimento! A solução com jQuery apontado por thg435fazia o que eu queria ...
amp
21

O que TJ Crowder disse, de qualquer forma, terá algum tipo de iteração oculta, com lodash isso se torna:

var index = _.findIndex(array, {prop2: 'yutu'})
aliak
fonte
1
enquanto você pode apenas loop através de várias maneiras para obter o índice, encontrar Index é a melhor solução, mesmo aprovada em ES6 em métodos de matriz nativas
Kelly Milligan
13
var CarId = 23;

//x.VehicleId property to match in the object array
var carIndex = CarsList.map(function (x) { return x.VehicleId; }).indexOf(CarId);

E para números de matriz básicos, você também pode fazer isso:

var numberList = [100,200,300,400,500];
var index = numberList.indexOf(200); // 1

Você receberá -1 se não conseguir encontrar um valor na matriz.

David Castro
fonte
11
var index;
yourArray.some(function (elem, i) {
    return elem.prop2 === 'yutu' ? (index = i, true) : false;
});

Iterar sobre todos os elementos da matriz. Retorna o índice e true ou false se a condição não corresponder.

Importante é o valor de retorno explícito de true (ou um valor cujo resultado booleano seja verdadeiro). A atribuição única não é suficiente, devido a um possível índice com 0 (Boolean (0) === false), que não resultaria em erro, mas desativa a interrupção da iteração.

Editar

Uma versão ainda mais curta do anterior:

yourArray.some(function (elem, i) {
    return elem.prop2 === 'yutu' && ~(index = i);
});
Nina Scholz
fonte
O que o ~ personagem faz no seu segundo trecho?
quer
@serkan, é um bit a bit NÃO| operador, que é uma versão curta de obtenção de um índice (com 1) um truthy / Falsas resultado, se existe um índice.
Nina Scholz
obrigado Nina, Sem o caractere ~, o código funciona como está, não é?
30518 serkan
@serkan, sua pergunta não é clara, mas sem ~ela não funciona assim.
Nina Scholz
1
ah, !!(index = 0)e !!~(index = 0)diferença mesmo. Obrigado!
30518 serkan
4

Você pode usar o Array.prototype.some () da seguinte maneira (conforme mencionado nas outras respostas):

https://jsfiddle.net/h1d69exj/2/

function findIndexInData(data, property, value) {
    var result = -1;
    data.some(function (item, i) {
        if (item[property] === value) {
            result = i;
            return true;
        }
    });
    return result;
}
var data = [{prop1:"abc",prop2:"qwe"},{prop1:"bnmb",prop2:"yutu"},{prop1:"zxvz",prop2:"qwrq"}]



alert(findIndexInData(data, 'prop2', "yutu")); // shows index of 1
GibboK
fonte
4

Eu já vi muitas soluções acima.

Aqui estou usando a função map para encontrar o índice do texto de pesquisa em um objeto de matriz.

Vou explicar minha resposta usando os dados dos alunos.

  • passo 1 : crie um objeto de matriz para os alunos (opcional, você pode criar seu próprio objeto de matriz).
    var students = [{name:"Rambabu",htno:"1245"},{name:"Divya",htno:"1246"},{name:"poojitha",htno:"1247"},{name:"magitha",htno:"1248"}];

  • etapa 2 : criar variável para pesquisar texto
    var studentNameToSearch = "Divya";

  • Etapa 3 : Crie uma variável para armazenar o índice correspondente (aqui usamos a função map para iterar).
    var matchedIndex = students.map(function (obj) { return obj.name; }).indexOf(studentNameToSearch);

var students = [{name:"Rambabu",htno:"1245"},{name:"Divya",htno:"1246"},{name:"poojitha",htno:"1247"},{name:"magitha",htno:"1248"}];

var studentNameToSearch = "Divya";

var matchedIndex = students.map(function (obj) { return obj.name; }).indexOf(studentNameToSearch);

console.log(matchedIndex);

alert("Your search name index in array is:"+matchedIndex)

Rambabu Bommisetti
fonte
3
function findIndexByKeyValue(_array, key, value) {
    for (var i = 0; i < _array.length; i++) { 
        if (_array[i][key] == value) {
            return i;
        }
    }
    return -1;
}
var a = [
    {prop1:"abc",prop2:"qwe"},
    {prop1:"bnmb",prop2:"yutu"},
    {prop1:"zxvz",prop2:"qwrq"}];
var index = findIndexByKeyValue(a, 'prop2', 'yutu');
console.log(index);
pranabesh chand
fonte
1

Por que você não deseja iterar exatamente? Os novos Array.prototype.forEach são ótimos para essa finalidade!

Você pode usar uma Árvore de Pesquisa Binária para encontrar através de uma única chamada de método, se desejar. Esta é uma implementação pura da árvore de pesquisa BTree e Red black em JS - https://github.com/vadimg/js_bintrees - mas não tenho certeza se você pode encontrar o índice ao mesmo tempo.

Rishabh
fonte
1

Uma etapa usando Array.reduce () - sem jQuery

var items = [{id: 331}, {id: 220}, {id: 872}];

var searchIndexForId = 220;
var index = items.reduce(function(searchIndex, item, index){
  if(item.id === searchIndexForId) { 
    console.log('found!');
    searchIndex = index;
  }
  return searchIndex;
}, null);

retornará nullse o índice não foi encontrado.

SagiSergeNadir
fonte
0
var list =  [
                {prop1:"abc",prop2:"qwe"},
                {prop1:"bnmb",prop2:"yutu"},
                {prop1:"zxvz",prop2:"qwrq"}
            ];

var findProp = p => {
    var index = -1;
    $.each(list, (i, o) => {
        if(o.prop2 == p) {
            index = i;
            return false; // break
        }
    });
    return index; // -1 == not found, else == index
}
Ruben Morales Felix
fonte
0

Georg já mencionou que o ES6 possui Array.findIndex para isso. E algumas outras respostas são uma solução alternativa para o ES5 usando o método Array.some.

Uma abordagem mais elegante pode ser

var index;
for(index = yourArray.length; index-- > 0 && yourArray[index].prop2 !== "yutu";);

Ao mesmo tempo, gostaria de enfatizar que o Array.some pode ser implementado com uma técnica de pesquisa binária ou outra eficiente. Portanto, ele pode ter um desempenho melhor em loop em algum navegador.

Sanjoy
fonte
0

Experimente este código

var x = [{prop1:"abc",prop2:"qwe"},{prop1:"bnmb",prop2:"yutu"},{prop1:"zxvz",prop2:"qwrq"}]
let index = x.findIndex(x => x.prop1 === 'zxvz')
Trilok Singh
fonte