Как найти индекс всех вхождений элемента в массив?

108

Я пытаюсь найти индекс всех экземпляров элемента, скажем «Nano», в массиве JavaScript.

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

Я пробовал jQuery.inArray или аналогично .indexOf () , но он дал только индекс последнего экземпляра элемента, то есть 5 в данном случае.

Как мне получить его для всех экземпляров?

Норбдум
источник

Ответы:

116

У .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, простой цикл for позволит выполнить ту же работу более эффективно, и его легче понять и, следовательно, проще поддерживать:

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 - Да, простой цикл for, выполняющий итерацию по массиву, тоже был бы проще, но, поскольку OP упомянул попытку использовать, .indexOf()я хотел показать, что он может выполнять эту работу. (Думаю, я решил, что OP может понять, как это сделать с помощью цикла for.) Конечно, есть и другие способы сделать это, например,Cars.reduce(function(a, v, i) { if (v==="Nano") a.push(i); return a; }, []);
nnnnnn
Я могу сказать, что вы из Северной Америки, потому что вы использовали indexesвместо indices: P
4castle
2
@ 4castle - Ха. Нет. «Индексы» и «индексы» верны, и я стараюсь их чередовать. Я никогда не думал об этом как о региональном диалекте. Интересный.
nnnnnn
Обратите внимание, что первый приведенный пример отлично подходит для строк и массивов. Второй работает только для массивов.
SethWhite
81

Другое альтернативное решение - использовать 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: Проверьте совместимость браузера для reduceметода и использования polyfill при необходимости.

VisioN
источник
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)
yckart
источник
3
отлично, работает.
Можете
2
@Muthu map(…)проверяет на каждой итерации равенство eи value. Когда они совпадают, возвращается индекс, в противном случае - пустая строка. Чтобы избавиться от этих ложных значений, filter(String)убедитесь, что результат содержит только значения типа строки, а НЕ пустые. filter(String)можно также записать как:filter(e => e !== '')
yckart
3
... или: String(thing)приводит что-либо к строке. Array#filterвозвращает массив всех значений, для которых условие является истинным . Поскольку пустые строки являются ложными , они НЕ включаются в массив.
yckart 06
Спасибо за объяснение, мне очень помогло
Мутамижчелван. 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);

РЕДАКТИРОВАТЬ: Если вам не нужно поддерживать 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);
}

Я бы не сказал, что это лучше других ответов. Просто интересно.

абальтер
источник
5

Я просто хочу обновиться другим простым способом.

Вы также можете использовать метод 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.
Зак Дельвенталь
1
О, смотрите, у вас есть решение, и мое похоже на него. Я должен был увидеть ваш, прежде чем писать свой. Циклов было так много, что я подумал: «Я мог бы дать свой собственный ответ за 2 секунды».
Майкл Пирсон
Да. Это в основном слишком сложные. Хорошая поправка.
Зак Дельвенталь
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'));

Briosheje
источник
0

Мы можем использовать Stack и вставлять "i" в стек каждый раз, когда мы встречаем условие "arr [i] == value"

Проверь это:

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
источник
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]
EbiPenMan
источник
-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

Если вы собираетесь использовать подчеркивание / lodash, вы можете сделать

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