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?

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

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.


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)

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); // []

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 : []);

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.


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)

3
const indexes = cars
    .map((car, i) => car === "Nano" ? i : null)
    .filter(i => i !== null)

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]

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


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

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]

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]

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


-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]

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
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.