Dapatkan penghitung loop / indeks menggunakan untuk ... sintaks dalam JavaScript

318

Peringatan:

pertanyaan masih berlaku untuk for…ofloop.> Jangan gunakan for…inuntuk beralih di atas Array , gunakan untuk beralih di atas properti suatu objek. Yang mengatakan, ini


Saya mengerti bahwa for…insintaks dasar dalam JavaScript terlihat seperti ini:

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

Tapi bagaimana cara mendapatkan counter loop / indeks ?

Saya tahu saya mungkin bisa melakukan sesuatu seperti:

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

Atau bahkan yang baik:

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

Tapi saya lebih suka menggunakan for-inloop yang lebih sederhana . Saya pikir mereka terlihat lebih baik dan lebih masuk akal.

Apakah ada cara yang lebih sederhana atau lebih elegan?


Dengan Python mudah:

for i, obj in enumerate(myArray):
    print i
hobbes3
sumber
6
Jangan gunakan untuk ... untuk array. Dan lagipula, itu berulang atas nama properti, bukan nilai properti.
Felix Kling
1
Ini array, bukan objek, kan? Jadi alert(obj),?
Rocket Hazmat

Jawaban:

547

for…initerates atas nama properti, bukan nilai, dan melakukannya dalam urutan yang tidak ditentukan (ya, bahkan setelah ES6). Anda seharusnya tidak menggunakannya untuk beralih pada array. Bagi mereka, ada forEachmetode ES5 yang meneruskan nilai dan indeks ke fungsi yang Anda berikan:

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

Atau ES6 Array.prototype.entries, yang sekarang memiliki dukungan di berbagai versi browser saat ini:

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

Untuk iterables secara umum (di mana Anda akan menggunakan for…ofloop daripada a for…in), tidak ada yang built-in, namun:

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

demo

Jika Anda benar-benar bermaksud for…in- menghitung properti - Anda perlu penghitung tambahan. Object.keys(obj).forEachbisa bekerja, tetapi hanya mencakup properti sendiri ; for…intermasuk properti enumerable di mana saja di rantai prototipe.

Ry-
sumber
2
Oh oke. Saya bingung. Saya pikir in-in JavaScript sama dengan Python. Terimakasih atas klarifikasinya.
hobbes3
1
@quantumpotato: lets adalah vardengan cakupan blok. consts tidak berubah.
Ry-
1
ini adalah jawaban terinci, terima kasih untuk itu. Benar-benar mengklarifikasi semua hal yang dibahas
Dheeraj Bhaskar
1
pertanyaan bodoh tapi apa arti sebenarnya dari% d dan% s, atau bisakah mereka menjadi huruf yang saya inginkan?
klewis
2
@klewis: %dmemformat integer dan %smemformat string. Mereka didasarkan pada printf . Sebuah spesifikasi sedang dalam proses di console.spec.whatwg.org/#formatter .
Ry-
163

Dalam ES6, baik untuk menggunakan for - of loop. Anda bisa mendapatkan indeks untuk seperti ini

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

Perhatikan bahwa Array.entries()mengembalikan iterator , yang memungkinkannya bekerja di for-of loop; jangan bingung dengan Object.entries () , yang mengembalikan array pasangan nilai kunci.

buru-buru
sumber
9
Ini adalah jawaban yang jauh lebih baik daripada yang diterima!
trusktr
3
Saya pikir solusi ini lebih baik daripada forEach yang satu ... Ini menggunakan nomral untuk ... dari sintaks loop, dan Anda tidak harus menggunakan fungsi terpisah. Dengan kata lain, secara sintaksis lebih baik. OP sepertinya menginginkan ini.
u8y7541
1
entries()adalah kembali obyek kosong: {}. Adakah yang tahu mengapa itu terjadi? My arrayadalah Array of Objects.
Joshua Pinter
@ JoshuaPinter coba Object.entries(array)alih-aliharray.entries()
tonyg
2
Seharusnya melakukannya, Joshua - objek adalah iterator, objek dengan next()metode yang akan mengembalikan entri berikutnya dalam array setiap kali dipanggil. Tidak ada data (terlihat) di dalamnya; Anda mendapatkan data di objek yang mendasarinya dengan menelepon next(), yang memang dilakukan di belakang layar. cc @tonyg
Shog9
26

Bagaimana dengan ini

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

Di mana array.forEachmetode ini memiliki indexparameter yang merupakan indeks dari elemen saat ini sedang diproses dalam array.

Sanjay Shr
sumber
1
jawaban terbaik di sini
codepleb
4
Jawaban yang dipilih telah diposting 6 tahun sebelum ini dan sudah memiliki hal yang sama di dalamnya ...
Deiv
Foreach tidak baik untuk optimasi, karena breaktidak tersedia.
smartworld-dm
20

Solusi untuk koleksi array kecil:

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

arr - ARRAY, obj - KUNCI dari elemen saat ini, i - COUNTER / INDEX

Perhatikan: Metode kunci () tidak tersedia untuk versi IE <9, Anda harus menggunakan kode Polyfill . https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/keys

Aku lahir
sumber
7
Saya akan menyarankan: gunakan penghitung sebagai gantinya, tambahkan itu dalam lingkaran.
mayankcpdixit
2
Menambahkan ke mayankcpdixit, gunakan penghitung karena indexOf dapat memiliki dampak kinerja negatif.
Dean Liu
1
Semakin besar objek, semakin lambat ini akan didapat. Ini tidak skala.
weltschmerz
2
Ini agak lambat dan rumit karena var i = 0;dan i++;lebih pendek dan lebih efisien. Plus itu tidak berfungsi untuk properti yang dapat dihitung yang bukan properti miliknya.
Ry-
1
@trusktr: Dan jika diperlukan ... Anda seharusnya masih tidak menggunakan ini. Ubah saja penghitung saat Anda mengubah koleksi. Jika tidak harus di tempat, lakukan transformasi fungsional yang bagus sebagai gantinya.
Ry-
13

For-in-loop berulang-ulang pada properti Obyek. Jangan menggunakannya untuk Array, bahkan jika kadang-kadang berfungsi.

Properti objek kemudian tidak memiliki indeks, mereka semua sama dan tidak perlu dijalankan melalui urutan yang ditentukan. Jika Anda ingin menghitung properti, Anda harus menyiapkan penghitung tambahan (seperti yang Anda lakukan pada contoh pertama).

lingkaran di atas Array:

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

loop di atas Obyek:

var o = {};
for (var prop in o) {
    prop // is the property name
    o[prop] // is the property value - the item
}
Bergi
sumber
3
Jangan pernah melakukan apa pun (var i=0; i<a.length; i++)sebagai sumber daya yang terbuang. Gunakan(var i=0, var len = a.length; i<len; i++)
Félix Sanz
16
@FelixSanz: Sumber daya limbah? Tidak mungkin. Itu adalah optimasi mikro prematur yang hampir tidak pernah diperlukan, dan var i=0; i<a.length; i++)merupakan pola loop standar yang dioptimalkan oleh setiap mesin javascript yang layak pula.
Bergi
3
@ Feliksanz: Ya, dan var i=0; i<a.length; i++ini praktik terbaik.
Bergi
1
CIUMAN . Jika Anda menulis loop di mana Anda benar-benar membutuhkan ini, Anda melakukan sesuatu yang salah, atau Anda memiliki argumen yang lebih baik untuk keperluannya daripada "praktik terbaik". Ya, ini adalah praktik standar, tetapi tidak untuk optimasi kinerja umum, tetapi hanya untuk optimasi mikro.
Bergi
3
KISS berlaku di mana-mana. Optimalisasi prematur adalah anti-praktik.
Bergi
7

Seperti yang orang lain katakan, Anda seharusnya tidak menggunakan for..in untuk beralih ke array.

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

Jika Anda ingin sintaks yang lebih bersih, Anda bisa menggunakan forEach:

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

Jika Anda ingin menggunakan metode ini, pastikan Anda menyertakan ES5 shim untuk menambahkan dukungan untuk browser lama.

Robert Messerle
sumber
2

Jawaban Diberikan oleh rushUp Benar tetapi ini akan lebih nyaman

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

Inilah fungsinya eachWithIndex yang berfungsi dengan apa pun yang dapat diubah.

Anda juga bisa menulis fungsi serupa eachWithKeyyang berfungsi dengan objek menggunakan 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)

Hal yang baik dengan generator adalah bahwa mereka malas dan dapat mengambil hasil generator lain sebagai argumen.

Rivenfall
sumber
1

Itu versi saya dari iterator komposit yang menghasilkan indeks dan nilai fungsi generator yang lewat dengan contoh pencarian utama (lambat):

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
}

akurtser
sumber
Mengapa Anda memiliki fungsi, eachWithIndex[Symbol.iterator]bukan hanya fungsi eachWithIndex? eachWithIndextidak memenuhi antarmuka iterable, yang merupakan inti dari Symbol.iterator.
Ry-
@ Ry- Tangkapan bagus, berubah eachWithIndexuntuk menerima iterable dan mengembalikan iterable komposit tertutup.
akurtser
1

Di atas semua jawaban yang sangat baik yang diposting semua orang, saya ingin menambahkan bahwa solusi yang paling performan adalah ES6 entries. Tampaknya kontraintuitif bagi banyak dev di sini, jadi saya membuat benchamrk perf ini .

masukkan deskripsi gambar di sini

~ 6 kali lebih cepat. Terutama karena tidak perlu: a) mengakses array lebih dari sekali dan, b) membuat indeks.

sospedra
sumber