Nhận bộ đếm / chỉ mục vòng lặp bằng cách sử dụng cho cú pháp của cú pháp trong JavaScript


318

Chú ý:

câu hỏi vẫn áp dụng cho for…ofcác vòng lặp.> Đừng sử dụng for…inđể lặp qua một mảng , hãy sử dụng nó để lặp lại các thuộc tính của một đối tượng. Điều đó nói rằng, điều này


Tôi hiểu rằng for…incú pháp cơ bản trong JavaScript trông như thế này:

for (var obj in myArray) {
    // ...
}

Nhưng làm thế nào để tôi có được bộ đếm / chỉ số vòng lặp ?

Tôi biết tôi có thể có thể làm một cái gì đó như:

var i = 0;
for (var obj in myArray) {
    alert(i)
    i++
}

Hoặc thậm chí là tốt cũ:

for (var i = 0; i < myArray.length; i++) {
    var obj = myArray[i]
    alert(i)
}

Nhưng tôi thà sử dụng for-invòng lặp đơn giản hơn . Tôi nghĩ rằng họ trông tốt hơn và có ý nghĩa hơn.

Có một cách đơn giản hay thanh lịch hơn?


Trong Python thật dễ dàng:

for i, obj in enumerate(myArray):
    print i

6
Đừng sử dụng cho ... trong các mảng. Và dù sao, nó lặp đi lặp lại qua các tên thuộc tính, không phải các giá trị của các thuộc tính.
Felix Kling

1
Đó là một mảng, không phải là một đối tượng, phải không? Vì vậy , alert(obj)?
Rocket Hazmat

Câu trả lời:


546

for…inLặp lại tên tài sản, không phải giá trị và thực hiện theo thứ tự không xác định (có, ngay cả sau ES6). Bạn không nên sử dụng nó để lặp qua các mảng. Đối với họ, có forEachphương thức ES5 chuyển cả giá trị và chỉ mục cho hàm bạn cung cấp cho nó:

var myArray = [123, 15, 187, 32];

myArray.forEach(function (value, i) {
    console.log('%d: %s', i, value);
});

// Outputs:
// 0: 123
// 1: 15
// 2: 187
// 3: 32

Hoặc ES6 Array.prototype.entries, hiện có hỗ trợ trên các phiên bản trình duyệt hiện tại:

for (const [i, value] of myArray.entries()) {
    console.log('%d: %s', i, value);
}

Đối với các iterables nói chung (nơi bạn sẽ sử dụng một for…ofvòng lặp chứ không phải a for…in), tuy nhiên, không có gì tích hợp:

function* enumerate(iterable) {
    let i = 0;

    for (const x of iterable) {
        yield [i, x];
        i++;
    }
}

for (const [i, obj] of enumerate(myArray)) {
    console.log(i, obj);
}

bản giới thiệu

Nếu bạn thực sự có ý nghĩa for…in- liệt kê các thuộc tính - bạn sẽ cần một bộ đếm bổ sung. Object.keys(obj).forEachcó thể làm việc, nhưng nó chỉ bao gồm các thuộc tính riêng ; for…inbao gồm vô số thuộc tính bất cứ nơi nào trên chuỗi nguyên mẫu.


2
Ồ được thôi. Tôi đã nhầm lẫn. Tôi nghĩ rằng for-in của JavaScript giống như của Python. Cảm ơn bạn đã làm rõ.
hobbes3

1
@quantumpotato: lets là vars với phạm vi khối. consts không thay đổi.
Ry-

1
Đây là một câu trả lời chi tiết, cảm ơn vì nó. Thực sự làm rõ tất cả những điều được thảo luận
Dheeraj Bhaskar

1
Câu hỏi ngu ngốc nhưng% d và% s thực sự đại diện cho điều gì, hoặc chúng có thể là bất kỳ chữ cái nào tôi muốn chúng là?
klewis

2
@klewis: %dđịnh dạng một số nguyên và %sđịnh dạng một chuỗi. Họ dựa trên printf . Một thông số đang được tiến hành tại console.spec.whatwg.org/#formatter .
Ry-

163

Trong ES6, sử dụng vòng lặp for là tốt. Bạn có thể lấy chỉ mục cho như thế này

for (let [index, val] of array.entries()) {
        // your code goes here    
}

Lưu ý rằng Array.entries()trả về một iterator , đó là những gì cho phép nó hoạt động trong vòng lặp for; đừng nhầm lẫn điều này với Object.entries () , trả về một mảng các cặp khóa-giá trị.


9
Đây là một câu trả lời tốt hơn nhiều so với câu trả lời được chấp nhận!
trusktr

3
Tôi nghĩ giải pháp này tốt hơn giải pháp forEach ... Nó sử dụng danh nghĩa cho ... của cú pháp vòng lặp và bạn không phải sử dụng một chức năng riêng biệt. Nói cách khác, nó tốt hơn về mặt cú pháp. OP dường như đã muốn điều này.
u8y7541

1
entries()đang trả về một đối tượng trống : {}. Bất cứ ý tưởng tại sao đó sẽ là? My arraylà một mảng các đối tượng.
Joshua Pinter

@JoshuaPinter thử Object.entries(array)thay vìarray.entries()
tonyg

2
Đáng lẽ phải làm điều đó, Joshua - đối tượng là một trình vòng lặp, một đối tượng với một next()phương thức sẽ trả về các mục tiếp theo trong mảng mỗi khi nó được gọi. Không có dữ liệu (hiển thị) trong đó; bạn có được dữ liệu trong đối tượng cơ bản bằng cách gọi next(), điều này sẽ xảy ra đằng sau hậu trường. cc @tonyg
Shog9

26

Còn cái này thì sao

let numbers = [1,2,3,4,5]
numbers.forEach((number, index) => console.log(`${index}:${number}`))

Trong array.forEachđó phương thức này có một indextham số là chỉ mục của phần tử hiện tại đang được xử lý trong mảng.


1
câu trả lời hay nhất ở đây
codepleb

4
Câu trả lời được chọn đã được đăng 6 năm trước câu trả lời này và có cùng một điều đã có trong đó ...
Deiv

Foreach không tốt cho việc tối ưu hóa, vì breakkhông có sẵn.
smartworld-dm

20

Giải pháp cho các bộ sưu tập mảng nhỏ:

for (var obj in arr) {
    var i = Object.keys(arr).indexOf(obj);
}

mảng - ARRAY, obj - KEY của phần tử hiện tại, i - COUNTER / INDEX

Lưu ý: Các phím phương thức () không khả dụng cho phiên bản IE <9, bạn nên sử dụng mã Polyfill . https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/keys


7
Tôi muốn đề xuất: sử dụng một bộ đếm thay thế, tăng nó trong vòng lặp.
mayankcpdixit 6/07/2015

2
Thêm vào mayankcpdixit, thay vào đó hãy sử dụng bộ đếm vì indexOf có thể có tác động tiêu cực.
Trưởng khoa Liu

1
Đối tượng càng lớn, điều này sẽ càng chậm. Điều này không mở rộng.
dchacke

2
Đây là loại chậm vô nghĩa và phức tạp vì var i = 0;i++;ngắn hơn và hiệu quả hơn. Thêm vào đó, nó không hoạt động cho vô số thuộc tính không thuộc tính riêng.
Ry-

1
@trusktr: Và nếu được yêu cầu thì bạn vẫn không nên sử dụng cái này. Chỉ cần thay đổi bộ đếm khi bạn thay đổi bộ sưu tập. Nếu nó không phải tại chỗ, thay vào đó hãy thực hiện một chuyển đổi chức năng tốt.
Ry-

13

Các vòng lặp lặp trong các thuộc tính của một đối tượng. Đừng sử dụng chúng cho Mảng, ngay cả khi đôi khi chúng hoạt động.

Các thuộc tính đối tượng sau đó không có chỉ mục, chúng đều bằng nhau và không bắt buộc phải chạy qua theo thứ tự xác định. Nếu bạn muốn đếm các thuộc tính, bạn sẽ phải thiết lập bộ đếm phụ (như bạn đã làm trong ví dụ đầu tiên).

lặp trên một mảng:

var a = [];
for (var i=0; i<a.length; i++) {
    i // is the index
    a[i] // is the item
}

lặp trên một đối tượng:

var o = {};
for (var prop in o) {
    prop // is the property name
    o[prop] // is the property value - the item
}

3
Không bao giờ làm (var i=0; i<a.length; i++)như là lãng phí tài nguyên. Sử dụng(var i=0, var len = a.length; i<len; i++)
Félix Sanz

16
@FelixSanz: Lãng phí tài nguyên? Không đời nào. Đó là một tối ưu hóa vi mô sớm mà hầu như không cần thiết và var i=0; i<a.length; i++)là mẫu vòng lặp tiêu chuẩn được tối ưu hóa bởi mọi công cụ javascript phong nha.
Bergi

3
@FelixSanz: Vâng, và var i=0; i<a.length; i++là cách thực hành tốt nhất.
Bergi

1
HÔN . Nếu bạn viết các vòng lặp khi bạn thực sự cần điều này, thì bạn đang làm điều gì đó sai, hoặc bạn có một lý lẽ tốt hơn cho sự cần thiết của nó hơn là "thực hành tốt nhất". Vâng, đó là một thông lệ tiêu chuẩn, nhưng không phải để tối ưu hóa hiệu suất chung, mà chỉ để tối ưu hóa vi mô.
Bergi

3
KISS áp dụng ở mọi nơi. Tối ưu hóa sớm là một chống thực hành.
Bergi

7

Như những người khác đã nói, bạn không nên sử dụng for..in để lặp lại trên một mảng.

for ( var i = 0, len = myArray.length; i < len; i++ ) { ... }

Nếu bạn muốn cú pháp sạch hơn, bạn có thể sử dụng forEach:

myArray.forEach( function ( val, i ) { ... } );

Nếu bạn muốn sử dụng phương pháp này, hãy đảm bảo rằng bạn bao gồm shim ES5 để thêm hỗ trợ cho các trình duyệt cũ hơn.


2

Trả lời Đưa ra bởi rushUp Đúng nhưng điều này sẽ thuận tiện hơn

for (let [index, val] of array.entries() || []) {
   // your code goes here    
}

1

Đây là một chức năng eachWithIndexhoạt động với bất cứ điều gì có thể lặp lại.

Bạn cũng có thể viết một hàm tương tự hoạt eachWithKeyđộng với các objets bằng cách sử dụng for...in.

// example generator (returns an iterator that can only be iterated once)
function* eachFromTo(start, end) { for (let i = start; i <= end; i++) yield i }

// convers an iterable to an array (potential infinite loop)
function eachToArray(iterable) {
    const result = []
    for (const val of iterable) result.push(val)
    return result
}

// yields every value and index of an iterable (array, generator, ...)
function* eachWithIndex(iterable) {
    const shared = new Array(2)
    shared[1] = 0
    for (shared[0] of iterable) {
        yield shared
        shared[1]++
    }
}

console.log('iterate values and indexes from a generator')
for (const [val, i] of eachWithIndex(eachFromTo(10, 13))) console.log(val, i)

console.log('create an array')
const anArray = eachToArray(eachFromTo(10, 13))
console.log(anArray)

console.log('iterate values and indexes from an array')
for (const [val, i] of eachWithIndex(anArray)) console.log(val, i)

Điều tốt với các máy phát điện là chúng lười biếng và có thể lấy kết quả của một máy phát điện khác làm đối số.


1

Đó là phiên bản trình lặp tổng hợp của tôi mang lại một chỉ mục và bất kỳ giá trị nào của hàm trình tạo được thông qua với một ví dụ về tìm kiếm chính (chậm):

const eachWithIndex = (iterable) => {
  return {
    *[Symbol.iterator]() {
      let i = 0
      for(let val of iteratable) {
        i++
          yield [i, val]
      }
    }
  }

}

const isPrime = (n) => {
  for (i = 2; i < Math.floor(Math.sqrt(n) + 1); i++) {
    if (n % i == 0) {
      return false
    }
  }
  return true
}

let primes = {
  *[Symbol.iterator]() {
    let candidate = 2
    while (true) {
      if (isPrime(candidate)) yield candidate
        candidate++
    }
  }
}

for (const [i, prime] of eachWithIndex(primes)) {
  console.log(i, prime)
  if (i === 100) break
}


Tại sao bạn có một chức năng eachWithIndex[Symbol.iterator]thay vì chỉ một chức năng eachWithIndex? eachWithIndexkhông thỏa mãn giao diện lặp, đó là toàn bộ điểm Symbol.iterator.
Ry-

@ Ry- Bắt tốt, thay đổi eachWithIndexđể chấp nhận lặp lại và trả lại một lần lặp tổng hợp đã đóng.
akurtser

1

Trên đầu những câu trả lời rất hay mà mọi người đã đăng, tôi muốn thêm rằng giải pháp hiệu quả nhất là ES6 entries. Có vẻ như trái ngược với nhiều nhà phát triển ở đây, vì vậy tôi đã tạo ra chiếc ghế dài hoàn hảo này .

nhập mô tả hình ảnh ở đây

Nó nhanh hơn ~ 6 lần. Chủ yếu là vì không cần: a) truy cập mảng nhiều lần và, b) bỏ chỉ mục.

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.