Làm thế nào để tìm chỉ số của tất cả các lần xuất hiện của phần tử trong mảng?


108

Tôi đang cố gắng tìm chỉ mục của tất cả các phiên bản của một phần tử, chẳng hạn như "Nano", trong một mảng JavaScript.

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

Tôi đã thử jQuery.inArray , hoặc tương tự, .indexOf () , nhưng nó chỉ cung cấp chỉ mục của phiên bản cuối cùng của phần tử, tức là 5 trong trường hợp này.

Làm cách nào để lấy nó cho tất cả các trường hợp?

Câu trả lời:


115

Các .indexOf()phương pháp có một tham số tùy chọn thứ hai đó quy định cụ thể các chỉ số để bắt đầu tìm kiếm từ, vì vậy bạn có thể gọi nó trong một vòng lặp để tìm tất cả các trường hợp của một giá trị cụ thể:

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

Bạn không thực sự nói rõ bạn muốn sử dụng các chỉ mục như thế nào, vì vậy hàm của tôi trả về chúng dưới dạng một mảng (hoặc trả về một mảng trống nếu không tìm thấy giá trị), nhưng bạn có thể làm điều gì đó khác với các giá trị chỉ mục riêng lẻ bên trong vòng lặp.

CẬP NHẬT: Theo nhận xét của VisioN, một vòng lặp for đơn giản sẽ thực hiện cùng một công việc hiệu quả hơn và dễ hiểu hơn và do đó dễ bảo trì hơn:

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

1
Nó dường như không phải là giải pháp thay thế nhanh hơn cho một forvòng lặp đơn lẻ với mảng chỉ mục được điền vào.
VisioN

1
@VisioN - Đúng, một vòng lặp for đơn giản lặp lại trên mảng cũng sẽ đơn giản hơn, nhưng vì OP đã đề cập đến việc cố gắng sử dụng .indexOf()nên tôi muốn chứng minh rằng nó có thể thực hiện công việc. (Tôi đoán tôi đã tìm OP có thể tìm ra cách để làm điều đó với một vòng lặp for.) Dĩ nhiên có những cách khác để làm điều đó, ví dụ như,Cars.reduce(function(a, v, i) { if (v==="Nano") a.push(i); return a; }, []);
nnnnnn

Tôi có thể nói bạn đến từ Bắc Mỹ bởi vì bạn sử dụng indexesthay vì indices: P
4castle

2
@ 4castle - Hà. Không, tôi không phải. "Chỉ số" và "chỉ mục" đều đúng và tôi có xu hướng thay thế giữa hai chỉ số. Tôi chưa bao giờ nghĩ đó là một thứ phương ngữ vùng miền. Hấp dẫn.
nnnnnn

Lưu ý rằng ví dụ đầu tiên được đưa ra hoạt động tốt cho chuỗi và mảng. Thứ hai chỉ hoạt động cho mảng.
SethWhite

80

Một giải pháp thay thế khác là sử dụng 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: Kiểm tra tính tương thích của trình duyệt để biết reducephương pháp và sử dụng polyfill nếu cần.


2
+1. Sự trùng hợp thú vị: Tôi vừa chỉnh sửa câu trả lời của tôi cho nhận xét của bạn dưới câu trả lời của tôi để đề xuất chính xác giải pháp này, sau đó tôi làm mới và thấy bạn đã mã hóa cùng một thứ với chỉ một tên biến khác nhau.
nnnnnn 27/12/13

@nnnnnn :)Vâng, tôi nghĩ có reducethể là một sự thay thế tốt.
VisioN

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

Tôi sử dụng googled contatchậm hơn push, do đó tôi gắn bó với câu trả lời.
Andre Elrico

54

Một cách tiếp cận khác sử dụng Array.prototype.map ()Array.prototype.filter () :

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

3
tuyệt vời, nó hoạt động. bạn có thể giải thích vai trò của bộ lọc (Chuỗi)
Muthamizhchelvan là gì. V

2
@Muthu map(…)kiểm tra mỗi lần lặp lại xem có bằng evalue. Khi chúng khớp với chỉ mục sẽ được trả về, nếu không thì sẽ là một chuỗi trống. Để loại bỏ các giá trị sai đó, filter(String)hãy đảm bảo rằng kết quả chỉ chứa các giá trị thuộc loại chuỗi và KHÔNG trống. filter(String)cũng có thể được viết là:filter(e => e !== '')
yckart

3
... hoặc: String(thing)ép buộc bất cứ thứ gì vào một chuỗi. Array#filtertrả về một mảng gồm tất cả các giá trị mà điều kiện là true . Bởi vì chuỗi rỗng là falsy , những là không bao gồm trong mảng.
yckart

Cảm ơn bạn đã giải thích, nó thực sự hữu ích cho tôi
Muthamizhchelvan. V

2
Tôi sẽ bối rối nếu tôi thấy điều này trong một dự án. Nó đọc giống như "Lọc thành chuỗi", có nghĩa là chỉ giữ lại nếu đó là một chuỗi. Và sau đó mảng kết quả sẽ là các chỉ mục dưới dạng chuỗi, không phải số.
Michael Pearson

14

Cách đơn giản hơn với phong cách 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

Bạn có thể viết một giải pháp đơn giản dễ đọc cho vấn đề này bằng cách sử dụng cả hai mapfilter:

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

CHỈNH SỬA: Nếu bạn không cần hỗ trợ IE / Edge (hoặc đang chuyển mã của bạn), ES2019 đã cung cấp cho chúng tôi flatMap , cho phép bạn thực hiện việc này trong một lớp lót đơn giản:

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

6

Lưu ý: MDN cung cấp một phương thức sử dụng vòng lặp 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);
}

Tôi sẽ không nói rằng nó tốt hơn bất kỳ câu trả lời nào khác. Chỉ thú vị.


4

Tôi chỉ muốn cập nhật bằng một phương pháp dễ dàng khác.

Bạn cũng có thể sử dụng phương thức 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
Chỉ số dựa trên số không, vì vậy điều này sẽ không thành công nếu chiếc xe đầu tiên là Nano.
Zac Delventhal

1
Ồ, bạn có một giải pháp và giải pháp của tôi trông giống như nó. Tôi lẽ ra phải nhìn thấy của bạn trước khi dành thời gian viết bài của tôi. Có rất nhiều vòng lặp for trải dài mà tôi nghĩ, "Tôi có thể đưa ra câu trả lời của riêng mình trong 2 giây."
Michael Pearson

Vâng. Đây hầu hết là cách quá phức tạp. Chỉnh sửa tốt đẹp.
Zac Delventhal

2

Điều này đã làm việc cho tôi:

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

Chỉ để chia sẻ một phương pháp khác, bạn cũng có thể sử dụng Trình tạo hàm để đạt được kết quả:

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

Chúng ta có thể sử dụng Stack và đẩy "i" vào ngăn xếp mỗi khi gặp điều kiện "arr [i] == value"

Kiểm tra điều này:

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
Câu hỏi được gắn thẻ javascript, trong khi câu trả lời của bạn là Javatôi tin?
noggin182

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

=> [0, 2]

Vui lòng viết giải thích cần thiết hoặc nhận xét nội tuyến cho mã. BTW, giải pháp của bạn đã hoạt động nhưng nó có 3 lần lặp lại ...
JustWe 22/11/19

0

Bạn có thể sử dụng 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;
};

}

Sử dụng nó như thế này:

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

result: [0, 3, 7]

-1

findIndexchỉ lấy ra chỉ mục đầu tiên phù hợp với đầu ra gọi lại. Bạn có thể triển khai findIndexesmảng của riêng mình bằng cách mở rộng Mảng, sau đó truyền các mảng của bạn sang cấu trúc mới.

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

Nếu bạn định sử dụng dấu gạch dưới / dấu gạch ngang, bạn có thể làm

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
Bạn không thực sự cần bất kỳ thư viện nào cho việc đó:(["Nano", "Volvo", "BMW", "Nano", "VW", "Nano"]).map((v, i)=> [i, v === "Nano"]).filter(v=>v[1]).map(v=>v[0])
edjroot 14/02
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.