Як знайти індекс усіх зустрічей елемента в масиві?


108

Я намагаюся знайти індекс усіх екземплярів елемента, скажімо, "Nano", у масиві JavaScript.

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

Я спробував jQuery.inArray , або подібним чином, .indexOf () , але він дав лише індекс останньої інстанції елемента, тобто 5 в цьому випадку.

Як отримати його для всіх примірників?

Відповіді:


115

У .indexOf()методі є необов'язковий другий параметр, який визначає індекс, з якого слід починати пошук, тому ви можете викликати його в циклі, щоб знайти всі екземпляри певного значення:

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

Ви насправді не даєте зрозуміти, як ви хочете використовувати індекси, тому моя функція повертає їх як масив (або повертає порожній масив, якщо значення не знайдено), але ви можете зробити щось інше із окремими значеннями індексу всередині петлі.

ОНОВЛЕННЯ: Відповідно до коментаря VisioN, простий цикл дозволить зробити ту саму роботу ефективніше, і її легше зрозуміти і, отже, легше підтримувати:

function getAllIndexes(arr, val) {
    var indexes = [], i;
    for(i = 0; i < arr.length; i++)
        if (arr[i] === val)
            indexes.push(i);
    return indexes;
}

1
Здається, це не є швидшою альтернативою одному forциклу з заповненням масиву індексів.
VisioN

1
@VisioN - Так, звичайна ітерація циклу через масив також була б простішою, але оскільки згадувана ОП намагалася використовувати, .indexOf()я хотів показати, що вона може виконати цю роботу. (Напевно, я подумав, що ОП може зрозуміти, як це зробити за допомогою циклу.) Звичайно, є й інші способи, наприклад,Cars.reduce(function(a, v, i) { if (v==="Nano") a.push(i); return a; }, []);
nnnnnn

Я можу сказати, що ви з Північної Америки, тому що ви використовували indexesзамість indices: P
4castle

2
@ 4castle - Ха. Ні. "Індекси" та "індекси" є правильними, і я схильний чергувати два. Я ніколи не думав про це як про регіональний діалект. Цікаво.
nnnnnn

Зауважимо, що перший приклад добре працює для рядків і масивів. Другий працює лише для масивів.
SethWhite

80

Іншим альтернативним рішенням є використання 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]

Примітка: Перевірте сумісність веб-переглядача щодо reduceметоду та за потреби використовуйте polyfill .


2
+1. Кумедний збіг обставин: Я щойно відредагував свою відповідь на ваш коментар під своєю відповіддю, щоб запропонувати саме це рішення, тоді я поновлююсь і бачу, що ви вже зашифрували одне і те ж із лише однією назвою змінної.
nnnnnn

@nnnnnn :)Так, я думав, може reduceбути приємною альтернативою.
VisioN

26
array.reduce((a, e, i) => (e === value) ? a.concat(i) : a, [])
yckart

Я гугл contatповільніше push, тому я дотримуюся відповіді.
Андре Елріко

54

Інший підхід із використанням Array.prototype.map () та Array.prototype.filter () :

var indices = array.map((e, i) => e === value ? i : '').filter(String)

3
чудово, це працює. чи можете ви пояснити, яка роль фільтра (String)
Мутаміжелван. V

2
@Muthu map(…)перевіряє кожну ітерацію на рівність eта value. Коли вони відповідають індексу, повертається порожній рядок. Щоб позбутися цих хибних значень, filter(String)переконайтеся, що результат містить лише значення, які є типом рядка, а НЕ порожніми. filter(String)можна також записати так:filter(e => e !== '')
yckart

3
... або: String(thing)примушує що-небудь до рядка. Array#filterповертає масив усіх значень, для яких умова є правдою . Оскільки порожні рядки є хибними , вони НЕ включені до масиву.
yckart

Дякую за ваше пояснення, мені це дуже корисно
Мутаміжелван. V

2
Я був би розгублений, якби це бачив у проекті. Він звучить як "Фільтр до рядків", тобто зберігати лише якщо це рядок. І тоді отриманий масив буде індексами як рядки, а не числа.
Майкл Пірсон

14

Більш простий спосіб у стилі 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

Ви можете написати просте зрозуміле рішення для цього, використовуючи обидва mapта filter:

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

EDIT: Якщо вам не потрібно підтримувати IE / Edge (або переносять ваш код), ES2019 надав нам flatMap , який дозволяє вам це зробити в простому однокласнішці :

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

6

Примітка: MDN дає метод, що використовує цикл 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);
}

Я б не сказав, що це краще, ніж інші відповіді. Просто цікаво.


4

Я просто хочу оновити ще одним простим методом.

Ви також можете використовувати метод 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
Індекси базуються на нулі, тому це не вдасться, якщо перший автомобіль буде Nano.
Zac Delventhal

1
О, дивіться, у вас є рішення, і моє виглядає так само, як воно. Я повинен був бачити твої, перш ніж я витратив час на написання свого. Якраз було так багато розповсюдження петель, що я думав: "Я міг зробити власну відповідь за 2 секунди".
Майкл Пірсон

Так. Здебільшого це складні способи. Приємна корекція.
Zac Delventhal

2

Це працювало для мене:

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

Просто для того, щоб поділитися іншим методом, ви можете використовувати функціональні генератори і для досягнення результату:

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

Ми можемо використовувати Stack і натискати "i" в стек щоразу, коли стикаємося з умовою "arr [i] == значення"

Перевір це:

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
Питання позначено тегом javascript, а чи Javaвважаю я вашу відповідь ?
noggin182

0
["a", "b", "a", "b"]
   .map((val, index) => ({ val, index }))
   .filter(({val, index}) => val === "a")
   .map(({val, index}) => index)

=> [0, 2]

Будь ласка, напишіть істотне пояснення або вбудовані коментарі до коду. До речі, ваше рішення спрацювало, але воно містить 3 ітерації ...
JustWe

0

Можна використовувати 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;
};

}

Використовуйте його так:

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

result: [0, 3, 7]

-1

findIndexотримує лише перший індекс, який відповідає виходу зворотного дзвінка. Ви можете реалізувати свій власний findIndexes, розширивши Array, а потім передати масиви новій структурі.

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

Якщо ви маєте намір підкреслити / подати, ви можете зробити це

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
Для цього вам не потрібна жодна бібліотека:(["Nano", "Volvo", "BMW", "Nano", "VW", "Nano"]).map((v, i)=> [i, v === "Nano"]).filter(v=>v[1]).map(v=>v[0])
edjroot
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.