Comment trouver l'index de toutes les occurrences d'élément dans le tableau?

108

J'essaie de trouver l'index de toutes les instances d'un élément, disons "Nano", dans un tableau JavaScript.

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

J'ai essayé jQuery.inArray , ou de manière similaire, .indexOf () , mais il n'a donné que l'index de la dernière instance de l'élément, soit 5 dans ce cas.

Comment puis-je l'obtenir pour toutes les instances?

Norbdum
la source

Réponses:

115

La .indexOf()méthode a un deuxième paramètre facultatif qui spécifie l'index à partir duquel commencer la recherche, vous pouvez donc l'appeler dans une boucle pour trouver toutes les instances d'une valeur particulière:

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

Vous n'indiquez pas vraiment comment vous voulez utiliser les index, donc ma fonction les renvoie sous forme de tableau (ou renvoie un tableau vide si la valeur n'est pas trouvée), mais vous pouvez faire autre chose avec les valeurs d'index individuelles à l'intérieur de la boucle.

MISE À JOUR: Selon le commentaire de VisioN, une simple boucle for ferait le même travail plus efficacement, et il est plus facile à comprendre et donc plus facile à maintenir:

function getAllIndexes(arr, val) {
    var indexes = [], i;
    for(i = 0; i < arr.length; i++)
        if (arr[i] === val)
            indexes.push(i);
    return indexes;
}
nnnnnn
la source
1
Cela ne semble pas être l'alternative la plus rapide à une forboucle unique avec un tableau d'indexation.
VisioN
1
@VisioN - Oui, une simple boucle pour itérer sur le tableau serait aussi plus simple, mais comme l'OP a mentionné essayer d'utiliser, .indexOf()je voulais montrer qu'il peut faire le travail. (Je suppose que j'ai pensé que l'OP pourrait trouver comment le faire avec une boucle for.) Bien sûr, il existe d'autres moyens de le faire, par exempleCars.reduce(function(a, v, i) { if (v==="Nano") a.push(i); return a; }, []);
nnnnnn
Je peux dire que vous venez d'Amérique du Nord parce que vous avez utilisé indexesau lieu de indices: P
4castle
2
@ 4castle - Ha. Non, je ne suis pas. «Indices» et «index» sont tous deux corrects, et j'ai tendance à alterner entre les deux. Je n'avais jamais pensé à cela comme à un dialecte régional. Intéressant.
nnnnnn
Notez que le premier exemple donné fonctionne très bien pour les chaînes et les tableaux. Le second ne fonctionne que pour les tableaux.
SethWhite
81

Une autre solution alternative consiste à utiliser 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: Vérifiez la compatibilité du navigateur pour la reduceméthode et utilisez polyfill si nécessaire.

Vision
la source
2
+1. Drôle de coïncidence: je viens de modifier ma réponse à votre commentaire sous ma réponse pour suggérer exactement cette solution, puis je rafraîchis et vois que vous avez déjà codé la même chose avec un seul nom de variable différent.
nnnnnn
@nnnnnn :)Ouais, j'ai pensé que ça reducepourrait être une bonne alternative.
VisioN
26
array.reduce((a, e, i) => (e === value) ? a.concat(i) : a, [])
yckart
Je googlé contatest plus lent que push, donc je m'en tiens à la réponse.
Andre Elrico
54

Une autre approche utilisant Array.prototype.map () et Array.prototype.filter () :

var indices = array.map((e, i) => e === value ? i : '').filter(String)
yckart
la source
3
super, ça marche. pouvez-vous expliquer quel est le rôle du filtre (String)
Muthamizhchelvan. V
2
@Muthu map(…)vérifie à chaque itération l'égalité de eet value. Lorsqu'ils correspondent, l'index est renvoyé, sinon une chaîne vide. Pour se débarrasser de ces valeurs fausses, filter(String)assurez-vous que le résultat ne contient que des valeurs de type chaîne et NON vides. filter(String)pourrait aussi s'écrire:filter(e => e !== '')
yckart
3
... ou: String(thing)contraint quoi que ce soit à une chaîne. Array#filterrenvoie un tableau de toutes les valeurs pour lesquelles la condition est véridique . Étant donné que les chaînes vides sont fausses , elles ne sont PAS incluses dans le tableau.
yckart
Merci pour votre explication, c'est vraiment utile pour moi
Muthamizhchelvan. V
2
Je serais confus si je voyais cela dans un projet. Il se lit comme "Filtrer les chaînes", ce qui signifie ne conserver que s'il s'agit d'une chaîne. Et puis le tableau résultant serait des index sous forme de chaînes, pas de nombres.
Michael Pearson le
14

Une manière plus simple avec le style 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
la source
12

Vous pouvez écrire une solution simple et lisible à cela en utilisant à la fois mapet filter:

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

EDIT: Si vous n'avez pas besoin de prendre en charge IE / Edge (ou si vous transpilez votre code), ES2019 nous a donné flatMap , qui vous permet de le faire en une seule ligne:

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

Remarque: MDN donne une méthode utilisant une boucle 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);
}

Je ne dirais pas que c'est mieux que d'autres réponses. Juste intéressant.

abalter
la source
4

Je veux juste mettre à jour avec une autre méthode simple.

Vous pouvez également utiliser la méthode forEach.

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

var result = [];

Cars.forEach((car, index) => car === 'Nano' ? result.push(index) : null)
Ted Khi
la source
3
const indexes = cars
    .map((car, i) => car === "Nano" ? i : null)
    .filter(i => i !== null)
Michael Pearson
la source
1
Les index sont basés sur zéro, donc cela échouera si la première voiture est une Nano.
Zac Delventhal
1
Oh regarde, tu as une solution et la mienne lui ressemble. J'aurais dû voir le vôtre avant de passer du temps à écrire le mien. Il y avait tellement de boucles for tentaculaires que j'ai pensé: "Je pourrais faire ma propre réponse en 2 secondes."
Michael Pearson le
Ouais. Ce sont pour la plupart beaucoup trop compliqués. Belle correction.
Zac Delventhal
2

Cela a fonctionné pour moi:

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
la source
1

Juste pour partager une autre méthode, vous pouvez également utiliser des générateurs de fonctions pour obtenir le résultat:

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
la source
0

Nous pouvons utiliser Stack et pousser "i" dans la pile chaque fois que nous rencontrons la condition "arr [i] == value"

Vérifie ça:

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
la source
2
La question est identifiée avec javascript, tandis que votre réponse est Javaje crois?
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
la source
Veuillez écrire une explication essentielle ou des commentaires en ligne pour le code. BTW, votre solution a fonctionné mais elle contient 3 itérations ...
JustWe
0

Vous pouvez utiliser 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;
};

}

Utilisez-le comme ceci:

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

result: [0, 3, 7]
EbiPenMan
la source
-1

findIndexrécupère uniquement le premier index qui correspond à la sortie de rappel. Vous pouvez implémenter le vôtre findIndexesen étendant Array, puis en convertissant vos tableaux dans la nouvelle structure.

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
la source
-1

Si vous avez l'intention d'utiliser un trait de soulignement / lodash, vous pouvez faire

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]
Zéro
la source
2
Vous n'avez pas vraiment besoin de bibliothèque pour cela:(["Nano", "Volvo", "BMW", "Nano", "VW", "Nano"]).map((v, i)=> [i, v === "Nano"]).filter(v=>v[1]).map(v=>v[0])
edjroot