Wie finde ich den Index aller Vorkommen von Elementen im Array?

107

Ich versuche, den Index aller Instanzen eines Elements, z. B. "Nano", in einem JavaScript-Array zu finden.

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

Ich habe jQuery.inArray oder ähnlich .indexOf () ausprobiert , aber es gab nur den Index der letzten Instanz des Elements an, dh 5 in diesem Fall.

Wie bekomme ich es für alle Instanzen?

norbdum
quelle

Antworten:

114

Die .indexOf()Methode verfügt über einen optionalen zweiten Parameter, der den Index angibt, von dem aus die Suche gestartet werden soll. Sie können ihn also in einer Schleife aufrufen, um alle Instanzen eines bestimmten Werts zu finden:

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

Sie machen nicht wirklich klar, wie Sie die Indizes verwenden möchten, daher gibt meine Funktion sie als Array zurück (oder gibt ein leeres Array zurück, wenn der Wert nicht gefunden wird), aber Sie könnten mit den einzelnen Indexwerten etwas anderes tun innerhalb der Schleife.

UPDATE: Gemäß dem Kommentar von VisioN würde eine einfache for-Schleife den gleichen Job effizienter erledigen, und es ist einfacher zu verstehen und daher einfacher zu warten:

function getAllIndexes(arr, val) {
    var indexes = [], i;
    for(i = 0; i < arr.length; i++)
        if (arr[i] === val)
            indexes.push(i);
    return indexes;
}
nnnnnn
quelle
1
Es scheint nicht die schnellere Alternative zu einer einzelnen forSchleife mit ausgefülltem Indexarray zu sein.
VisioN
1
@VisioN - Ja, eine einfache for-Schleife, die über das Array iteriert, wäre auch einfacher, aber da das OP den Versuch der Verwendung erwähnte, .indexOf()wollte ich zeigen, dass es die Aufgabe erfüllen kann. (Ich denke, ich dachte, das OP könnte herausfinden, wie es mit einer for-Schleife gemacht wird.) Natürlich gibt es auch andere Möglichkeiten, z. B.Cars.reduce(function(a, v, i) { if (v==="Nano") a.push(i); return a; }, []);
nnnnnn
Ich kann Ihnen sagen, dass Sie aus Nordamerika kommen, weil Sie indexesanstelle von indices: P
4castle
2
@ 4castle - Ha. Nein, bin ich nicht. "Indizes" und "Indizes" sind beide korrekt, und ich neige dazu, zwischen den beiden zu wechseln. Ich hatte das nie als regionale Dialektsache angesehen. Interessant.
nnnnnn
Beachten Sie, dass das erste Beispiel hervorragend für Strings und Arrays geeignet ist. Die zweite funktioniert nur für Arrays.
SethWhite
79

Eine andere alternative Lösung ist die Verwendung von 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: Überprüfen Sie die Browserkompatibilität für die reduceMethode und verwenden Sie bei Bedarf Polyfill .

Vision
quelle
2
+1. Lustiger Zufall: Ich habe gerade meine Antwort auf Ihren Kommentar unter meiner Antwort bearbeitet, um genau diese Lösung vorzuschlagen. Dann aktualisiere ich und sehe, dass Sie dasselbe bereits mit nur einem anderen Variablennamen codiert haben.
nnnnnn
@nnnnnn :)Ja, ich dachte, vielleicht reducekönnte eine schöne Alternative sein.
VisioN
26
array.reduce((a, e, i) => (e === value) ? a.concat(i) : a, [])
Yckart
Ich habe gegoogelt contatist langsamer als push, deshalb bleibe ich bei der Antwort.
Andre Elrico
54

Ein anderer Ansatz mit Array.prototype.map () und Array.prototype.filter () :

var indices = array.map((e, i) => e === value ? i : '').filter(String)
yckart
quelle
3
toll, es funktioniert.
Können
2
@Muthu map(…)überprüft bei jeder Iteration die Gleichheit von eund value. Wenn sie mit dem Index übereinstimmen, wird eine leere Zeichenfolge zurückgegeben. Stellen Sie filter(String)sicher, dass das Ergebnis nur Werte enthält, die vom Typ Zeichenfolge sind und NICHT leer , um diese falschen Werte zu entfernen . filter(String)könnte auch geschrieben werden als:filter(e => e !== '')
yckart
3
... oder: String(thing)zwingt irgendetwas zu einer Zeichenfolge. GibtArray#filter ein Array aller Werte zurück, für die die Bedingung wahr ist . Da leere Zeichenfolgen falsch sind , sind diese NICHT im Array enthalten.
Yckart
Vielen Dank für Ihre Erklärung, es ist wirklich hilfreich für mich
Muthamizhchelvan. V
2
Ich wäre verwirrt, wenn ich das in einem Projekt sehen würde. Es liest sich wie "Nach Zeichenfolgen filtern", dh nur behalten, wenn es sich um eine Zeichenfolge handelt. Und dann wäre das resultierende Array Indizes als Zeichenfolgen, nicht als Zahlen.
Michael Pearson
13

Einfacher Weg mit es6-Stil.

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
quelle
12

Sie können eine einfache lesbare Lösung dafür schreiben, indem Sie sowohl mapals auch filter:

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

BEARBEITEN: Wenn Sie IE / Edge nicht unterstützen müssen (oder Ihren Code transpilieren), hat uns ES2019 flatMap zur Verfügung gestellt , mit der Sie dies in einem einfachen Einzeiler tun können:

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

Hinweis: MDN gibt eine Methode mit einer while-Schleife an :

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

Ich würde nicht sagen, dass es besser ist als andere Antworten. Nur interessant.

abalter
quelle
4

Ich möchte nur mit einer anderen einfachen Methode aktualisieren.

Sie können auch die forEach-Methode verwenden.

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

var result = [];

Cars.forEach((car, index) => car === 'Nano' ? result.push(index) : null)
Ted Khi
quelle
3
const indexes = cars
    .map((car, i) => car === "Nano" ? i : null)
    .filter(i => i !== null)
Michael Pearson
quelle
1
Die Indizes basieren auf Null, daher schlägt dies fehl, wenn das erste Auto ein Nano ist.
Zac Delventhal
1
Oh schau, du hast eine Lösung und meine sieht genauso aus. Ich hätte deine sehen sollen, bevor ich meine geschrieben habe. Es gab so viele Schleifen, dass ich dachte: "Ich könnte in 2 Sekunden meine eigene Antwort geben."
Michael Pearson
Ja. Diese sind meist viel zu kompliziert. Schöne Korrektur.
Zac Delventhal
2

Das hat bei mir funktioniert:

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
quelle
1

Um eine andere Methode zu teilen, können Sie auch Funktionsgeneratoren verwenden , um das Ergebnis zu erzielen:

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
quelle
0

Wir können Stack verwenden und "i" jedes Mal in den Stack schieben, wenn wir auf die Bedingung "arr [i] == value" stoßen.

Überprüfen Sie dies:

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
quelle
2
Die Frage ist markiert mit javascript, während Ihre Antwort Javaich glaube?
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
quelle
Bitte schreiben Sie eine wesentliche Erklärung oder Inline-Kommentare für den Code. Übrigens, Ihre Lösung hat funktioniert, aber sie enthält 3 Iterationen ...
JustWe
0

Sie können Polyfill verwenden

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

}}

Verwenden Sie es so:

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

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

findIndexRuft nur den ersten Index ab, der der Rückrufausgabe entspricht. Sie können Ihr eigenes implementieren, findIndexesindem Sie Array erweitern und dann Ihre Arrays in die neue Struktur umwandeln.

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
quelle
-1

Wenn Sie Unterstrich / lodash verwenden möchten, können Sie dies tun

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]
Null
quelle
2
Sie brauchen wirklich keine Bibliothek dafür:(["Nano", "Volvo", "BMW", "Nano", "VW", "Nano"]).map((v, i)=> [i, v === "Nano"]).filter(v=>v[1]).map(v=>v[0])
Edjroot