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?

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

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.


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)

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

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

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.


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)

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

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]

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


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

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]

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]

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


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

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
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.