Jak znaleźć indeks wszystkich wystąpień elementu w tablicy?

108

Próbuję znaleźć indeks wszystkich wystąpień elementu, powiedzmy „Nano”, w tablicy JavaScript.

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

Próbowałem jQuery.inArray , lub podobnie .indexOf () , ale podał on tylko indeks ostatniej instancji elementu, czyli w tym przypadku 5.

Jak mogę to uzyskać dla wszystkich instancji?

norbdum
źródło

Odpowiedzi:

116

.indexOf()Metoda ma opcjonalny drugi parametr, który określa indeks, aby rozpocząć wyszukiwanie od tak można nazwać to w pętli, aby znaleźć wszystkie wystąpienia określonej wartości:

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

Naprawdę nie wyjaśniasz, jak chcesz używać indeksów, więc moja funkcja zwraca je jako tablicę (lub zwraca pustą tablicę, jeśli wartość nie zostanie znaleziona), ale możesz zrobić coś innego z indywidualnymi wartościami indeksu wewnątrz pętli.

AKTUALIZACJA: zgodnie z komentarzem VisioN, prosta pętla for wykonałaby to samo zadanie wydajniej i jest łatwiejsza do zrozumienia, a zatem łatwiejsza w utrzymaniu:

function getAllIndexes(arr, val) {
    var indexes = [], i;
    for(i = 0; i < arr.length; i++)
        if (arr[i] === val)
            indexes.push(i);
    return indexes;
}
nnnnnn
źródło
1
Wydaje się, że nie jest to szybsza alternatywa dla pojedynczej forpętli z wypełnioną tablicą indeksów.
VisioN
1
@VisioN - Tak, zwykłe iterowanie pętli po tablicy byłoby prostsze, ale ponieważ OP wspomniał o próbie użycia .indexOf(), chciałem pokazać, że może wykonać zadanie. (Myślę, że pomyślałem, że OP może wymyślić, jak to zrobić za pomocą pętli for.) Oczywiście są inne sposoby, aby to zrobić, np.Cars.reduce(function(a, v, i) { if (v==="Nano") a.push(i); return a; }, []);
nnnnnn
Mogę powiedzieć, że jesteś z Ameryki Północnej, ponieważ indexeszamiast indices: P
4castle,
2
@ 4castle - Ha. Nie, nie jestem. Zarówno „indeksy”, jak i „indeksy” są poprawne i mam tendencję do przełączania się między nimi. Nigdy nie myślałem o tym jako o regionalnym dialekcie. Ciekawy.
nnnnnn
Zauważ, że pierwszy podany przykład działa świetnie dla łańcuchów i tablic. Drugi działa tylko dla tablic.
SethWhite
81

Innym alternatywnym rozwiązaniem jest zastosowanie 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]

Uwaga: Sprawdź zgodność przeglądarki pod kątem reducemetody i użyj wypełnienia, jeśli jest to wymagane.

Wizja
źródło
2
+1. Zabawny zbieg okoliczności: właśnie zredagowałem swoją odpowiedź na Twój komentarz pod moją odpowiedzią, aby zasugerować dokładnie to rozwiązanie, a następnie odświeżam i widzę, że zakodowałeś już to samo z inną nazwą tylko jednej zmiennej.
nnnnnn
@nnnnnn :)Tak, pomyślałem, że może reducebyć fajną alternatywą.
VisioN
26
array.reduce((a, e, i) => (e === value) ? a.concat(i) : a, [])
yckart
Wyszukiwanie w Google contatjest wolniejsze niż push, dlatego trzymam się odpowiedzi.
Andre Elrico,
54

Inne podejście wykorzystujące Array.prototype.map () i Array.prototype.filter () :

var indices = array.map((e, i) => e === value ? i : '').filter(String)
yckart
źródło
3
świetnie, to działa. czy możesz wyjaśnić, jaka jest rola filtra (String)
Muthamizhchelvan. V
2
@Muthu map(…)sprawdza w każdej iteracji równość ei value. Kiedy pasują do indeksu, zwracany jest pusty ciąg. Aby pozbyć się tych fałszywych wartości, filter(String)upewnij się, że wynik zawiera tylko wartości typu łańcuchowego, a NIE puste. filter(String)można również zapisać jako:filter(e => e !== '')
yckart
3
... lub: String(thing)wymusza cokolwiek na łańcuchu. Array#filterzwraca tablicę wszystkich wartości, dla których warunek jest prawdziwy . Ponieważ puste łańcuchy są fałszywe , NIE są one uwzględniane w tablicy.
yckart
Dziękuję za wyjaśnienie, jest to dla mnie bardzo pomocne
Muthamizhchelvan. V
2
Byłbym zdezorientowany, gdybym zobaczył to w projekcie. Brzmi jak „Filtruj do łańcuchów”, co oznacza, że ​​zachowaj tylko, jeśli jest to ciąg. A wynikowa tablica byłaby indeksami jako łańcuchami, a nie liczbami.
Michael Pearson
14

Prostszy sposób w stylu 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); // []
Michaił Gorelyshev
źródło
12

Możesz napisać proste, czytelne rozwiązanie, używając obu mapi filter:

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

EDYCJA: Jeśli nie potrzebujesz obsługi IE / Edge (lub transpilujesz swój kod), ES2019 dał nam flatMap , która pozwala to zrobić w prostym , jednowierszowym :

const nanoIndexes = Cars.flatMap((car, i) => car === 'Nano' ? i : []);
Zac Delventhal
źródło
6

Uwaga: MDN podaje metodę wykorzystującą pętlę 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);
}

Nie powiedziałbym, że to lepsze niż inne odpowiedzi. Po prostu interesujące.

abalter
źródło
4

Chcę tylko zaktualizować inną prostą metodę.

Możesz także użyć metody forEach.

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

var result = [];

Cars.forEach((car, index) => car === 'Nano' ? result.push(index) : null)
Ted Khi
źródło
3
const indexes = cars
    .map((car, i) => car === "Nano" ? i : null)
    .filter(i => i !== null)
Michael Pearson
źródło
1
Indeksy są liczone od zera, więc to się nie powiedzie, jeśli pierwszy samochód to Nano.
Zac Delventhal,
1
Spójrz, masz rozwiązanie, a moje wygląda tak samo. Powinienem był zobaczyć twoje, zanim spędziłem czas na pisaniu moich. Było tak wiele rozwalających się pętli, że pomyślałem: „Mogę udzielić własnej odpowiedzi w 2 sekundy”.
Michael Pearson
Tak. Są to przeważnie zbyt skomplikowane. Niezła korekta.
Zac Delventhal
2

To zadziałało dla mnie:

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
źródło
1

Aby udostępnić inną metodę, możesz również użyć generatorów funkcji, aby osiągnąć wynik:

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
źródło
0

Możemy użyć Stack i włożyć „i” do stosu za każdym razem, gdy napotkamy warunek „arr [i] == wartość”

Sprawdź to:

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
źródło
2
Pytanie jest oznaczone tagiem javascript, podczas gdy twoja odpowiedź jest Javajak wierzę?
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
źródło
Wpisz istotne wyjaśnienie lub komentarze w wierszu kodu. Przy okazji, twoje rozwiązanie zadziałało, ale zawiera 3 iteracje ...
JustWe,
0

Możesz użyć 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;
};

}

Użyj tego w ten sposób:

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

result: [0, 3, 7]
EbiPenMan
źródło
-1

findIndexpobiera tylko pierwszy indeks, który pasuje do danych wyjściowych wywołania zwrotnego. Możesz zaimplementować własne findIndexes, rozszerzając Array, a następnie rzutując tablice na nową strukturę.

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
źródło
-1

Jeśli zamierzasz użyć podkreślenia / lodash, możesz to zrobić

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
źródło
2
Naprawdę nie potrzebujesz do tego żadnej biblioteki:(["Nano", "Volvo", "BMW", "Nano", "VW", "Nano"]).map((v, i)=> [i, v === "Nano"]).filter(v=>v[1]).map(v=>v[0])
edjroot