Como encontrar o índice de todas as ocorrências do elemento na matriz?

108

Estou tentando encontrar o índice de todas as instâncias de um elemento, digamos, "Nano", em uma matriz JavaScript.

var Cars = ["Nano", "Volvo", "BMW", "Nano", "VW", "Nano"];

Tentei jQuery.inArray , ou similarmente, .indexOf () , mas deu apenas o índice da última instância do elemento, ou seja, 5 neste caso.

Como faço para obtê-lo para todas as instâncias?

Norbdum
fonte

Respostas:

115

O .indexOf()método tem um segundo parâmetro opcional que especifica o índice a partir do qual iniciar a pesquisa, para que você possa chamá-lo em um loop para encontrar todas as instâncias de um determinado valor:

function getAllIndexes(arr, val) {
    var indexes = [], i = -1;
    while ((i = arr.indexOf(val, i+1)) != -1){
        indexes.push(i);
    }
    return indexes;
}

var indexes = getAllIndexes(Cars, "Nano");

Você realmente não deixa claro como deseja usar os índices, então minha função os retorna como uma matriz (ou retorna uma matriz vazia se o valor não for encontrado), mas você poderia fazer outra coisa com os valores de índice individuais dentro do loop.

ATUALIZAÇÃO: de acordo com o comentário do VisioN, um loop for simples faria o mesmo trabalho com mais eficiência e é mais fácil de entender e, portanto, mais fácil de manter:

function getAllIndexes(arr, val) {
    var indexes = [], i;
    for(i = 0; i < arr.length; i++)
        if (arr[i] === val)
            indexes.push(i);
    return indexes;
}
nnnnnn
fonte
1
Não parece ser a alternativa mais rápida para um único forloop com preenchimento de matriz de índice.
VisioN
1
@VisioN - Sim, um simples loop for iterando sobre o array também seria mais simples, mas como o OP mencionou tentar usar, .indexOf()eu queria mostrar que ele pode fazer o trabalho. (Acho que descobri que o OP poderia descobrir como fazê-lo com um loop for.) Claro que existem outras maneiras de fazer isso, por exemplo,Cars.reduce(function(a, v, i) { if (v==="Nano") a.push(i); return a; }, []);
nnnnnn
Posso dizer que você é da América do Norte porque você usou em indexesvez de indices: P
4castle de
2
@ 4castle - Ha. Não, eu não sou. "Índices" e "índices" estão corretos e tendo a alternar entre os dois. Eu nunca pensei nisso como uma coisa de dialeto regional. Interessante.
nnnnnn
Observe que o primeiro exemplo fornecido funciona muito bem para strings e arrays. O segundo funciona apenas para matrizes.
SethWhite,
80

Outra solução alternativa é usar Array.prototype.reduce():

["Nano","Volvo","BMW","Nano","VW","Nano"].reduce(function(a, e, i) {
    if (e === 'Nano')
        a.push(i);
    return a;
}, []);   // [0, 3, 5]

NB: Verifique a compatibilidade do navegador para o reducemétodo e use polyfill se necessário.

Visão
fonte
2
+1. Coincidência engraçada: acabei de editar minha resposta ao seu comentário em minha resposta para sugerir exatamente essa solução, então eu atualizo e vejo que você já codificou a mesma coisa com apenas um nome de variável diferente.
nnnnnn
@nnnnnn :)Sim, pensei que talvez reducepudesse ser uma boa alternativa.
VisioN
26
array.reduce((a, e, i) => (e === value) ? a.concat(i) : a, [])
Yckart
Pesquisei no Google contaté mais lento do que push, portanto, fico com a resposta.
Andre Elrico
54

Outra abordagem usando Array.prototype.map () e Array.prototype.filter () :

var indices = array.map((e, i) => e === value ? i : '').filter(String)
Yckart
fonte
3
ótimo, funciona. você pode explicar qual é a função do filtro (String)
Muthamizhchelvan. V
2
@Muthu map(…)verifica em cada iteração a igualdade de ee value. Quando eles correspondem, o índice é retornado, caso contrário, uma string vazia. Para se livrar desses valores falsos, filter(String)certifique-se de que o resultado contenha apenas valores do tipo string e NÃO vazios. filter(String)também pode ser escrito como:filter(e => e !== '')
yckart
3
... ou: String(thing)força qualquer coisa a uma string. Array#filterretorna uma matriz de todos os valores para os quais a condição é verdadeira . Como as strings vazias são falsas , elas NÃO são incluídas na matriz.
Yckart
Obrigado pela sua explicação, é muito útil para mim
Muthamizhchelvan. V
2
Eu ficaria confuso se visse isso em um projeto. Lê-se como "Filtrar para strings", significando manter apenas se for uma string. E então a matriz resultante seria índices como strings, não números.
Michael Pearson
14

Maneira mais simples com o estilo es6.

const indexOfAll = (arr, val) => arr.reduce((acc, el, i) => (el === val ? [...acc, i] : acc), []);


//Examples:
var cars = ["Nano", "Volvo", "BMW", "Nano", "VW", "Nano"];
indexOfAll(cars, "Nano"); //[0, 3, 5]
indexOfAll([1, 2, 3, 1, 2, 3], 1); // [0,3]
indexOfAll([1, 2, 3], 4); // []
Mikhail Gorelyshev
fonte
12

Você pode escrever uma solução legível simples para isso usando mape filter:

const nanoIndexes = Cars
  .map((car, i) => car === 'Nano' ? i : -1)
  .filter(index => index !== -1);

EDITAR: Se você não precisa suportar IE / Edge (ou está transpilando seu código), ES2019 nos deu flatMap , que permite fazer isso em uma linha simples:

const nanoIndexes = Cars.flatMap((car, i) => car === 'Nano' ? i : []);
Zac Delventhal
fonte
6

Nota: MDN fornece um método usando um loop while :

var indices = [];
var array = ['a', 'b', 'a', 'c', 'a', 'd'];
var element = 'a';
var idx = array.indexOf(element);
while (idx != -1) {
  indices.push(idx);
  idx = array.indexOf(element, idx + 1);
}

Eu não diria que é melhor do que outras respostas. Apenas interessante.

abalter
fonte
4

Eu só quero atualizar com outro método fácil.

Você também pode usar o método forEach.

var Cars = ["Nano", "Volvo", "BMW", "Nano", "VW", "Nano"];

var result = [];

Cars.forEach((car, index) => car === 'Nano' ? result.push(index) : null)
Ted Khi
fonte
3
const indexes = cars
    .map((car, i) => car === "Nano" ? i : null)
    .filter(i => i !== null)
Michael Pearson
fonte
1
Os índices são baseados em zero, então isso falhará se o primeiro carro for um Nano.
Zac Delventhal
1
Olha, você tem uma solução e a minha se parece com ela. Eu deveria ter visto o seu antes de passar um tempo escrevendo o meu. Havia tantos loops espalhados que pensei: "Eu poderia fazer minha própria resposta em 2 segundos."
Michael Pearson
Sim. Estes são na maioria muito complicados. Boa correção.
Zac Delventhal
2

Isso funcionou para mim:

let array1 = [5, 12, 8, 130, 44, 12, 45, 12, 56];
let numToFind = 12
let indexesOf12 = [] // the number whose occurrence in the array we want to find

array1.forEach(function(elem, index, array) {
    if (elem === numToFind) {indexesOf12.push(index)}
    return indexesOf12
})

console.log(indexesOf12) // outputs [1, 5, 7]
Jona Dev
fonte
1

Apenas para compartilhar outro método, você também pode usar Geradores de Função para obter o resultado:

function findAllIndexOf(target, needle) {
  return [].concat(...(function*(){
    for (var i = 0; i < target.length; i++) if (target[i] === needle) yield [i];
  })());
}

var target = "hellooooo";
var target2 = ['w','o',1,3,'l','o'];

console.log(findAllIndexOf(target, 'o'));
console.log(findAllIndexOf(target2, 'o'));

Briosheje
fonte
0

Podemos usar Stack e inserir "i" na pilha sempre que encontrarmos a condição "arr [i] == value"

Verifique isto:

static void getindex(int arr[], int value)
{
    Stack<Integer>st= new Stack<Integer>();
    int n= arr.length;
    for(int i=n-1; i>=0 ;i--)
    {
        if(arr[i]==value)
        {
            st.push(i);
        }
    }   
    while(!st.isEmpty())
    {
        System.out.println(st.peek()+" ");
        st.pop(); 
    }
}
S Banzal
fonte
2
A pergunta está marcada com javascript, enquanto sua resposta é Javaeu acredito?
noggin182
0
["a", "b", "a", "b"]
   .map((val, index) => ({ val, index }))
   .filter(({val, index}) => val === "a")
   .map(({val, index}) => index)

=> [0, 2]
Dávid Konkoly
fonte
Escreva uma explicação essencial ou comentários embutidos para o código. BTW, sua solução funcionou, mas contém 3 iterações ...
JustWe
0

Você pode usar Polyfill

if (!Array.prototype.filterIndex) {
Array.prototype.filterIndex = function (func, thisArg) {

    'use strict';
    if (!((typeof func === 'Function' || typeof func === 'function') && this))
        throw new TypeError();

    let len = this.length >>> 0,
        res = new Array(len), // preallocate array
        t = this, c = 0, i = -1;

    let kValue;
    if (thisArg === undefined) {
        while (++i !== len) {
            // checks to see if the key was set
            if (i in this) {
                kValue = t[i]; // in case t is changed in callback
                if (func(t[i], i, t)) {
                    res[c++] = i;
                }
            }
        }
    }
    else {
        while (++i !== len) {
            // checks to see if the key was set
            if (i in this) {
                kValue = t[i];
                if (func.call(thisArg, t[i], i, t)) {
                    res[c++] = i;
                }
            }
        }
    }

    res.length = c; // shrink down array to proper size
    return res;
};

}

Use-o assim:

[2,23,1,2,3,4,52,2].filterIndex(element => element === 2)

result: [0, 3, 7]
EbiPenMan
fonte
-1

findIndexrecupera apenas o primeiro índice que corresponde à saída de retorno de chamada. Você pode implementar seu próprio findIndexesestendendo Array e, em seguida, convertendo seus arrays para a nova estrutura.

class EnhancedArray extends Array {
  findIndexes(where) {
    return this.reduce((a, e, i) => (where(e, i) ? a.concat(i) : a), []);
  }
}
   /*----Working with simple data structure (array of numbers) ---*/

//existing array
let myArray = [1, 3, 5, 5, 4, 5];

//cast it :
myArray = new EnhancedArray(...myArray);

//run
console.log(
   myArray.findIndexes((e) => e===5)
)
/*----Working with Array of complex items structure-*/

let arr = [{name: 'Ahmed'}, {name: 'Rami'}, {name: 'Abdennour'}];

arr= new EnhancedArray(...arr);


console.log(
  arr.findIndexes((o) => o.name.startsWith('A'))
)

Abdennour TOUMI
fonte
-1

Se você pretende usar sublinhado / lodash, você pode fazer

var Cars = ["Nano", "Volvo", "BMW", "Nano", "VW", "Nano"];

_.chain(Cars).map((v, i)=> [i, v === "Nano"]).filter(v=>v[1]).map(v=>v[0]).value()

[0, 3, 5]
Zero
fonte
2
Você realmente não precisa de nenhuma biblioteca para isso:(["Nano", "Volvo", "BMW", "Nano", "VW", "Nano"]).map((v, i)=> [i, v === "Nano"]).filter(v=>v[1]).map(v=>v[0])
edjroot