JavaScriptのfor…of構文を使用してループカウンター/インデックスを取得する


318

注意:

質問は依然としてfor…ofループに適用されます。> Arrayのfor…in反復には使用せず、オブジェクトのプロパティの反復に使用してください。つまり、これ


for…inJavaScript の基本的な構文が次のようになっていることを理解しています。

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

しかし、ループカウンター/インデックスを取得するにはどうすればよいですか?

私はおそらく私が次のようなことをすることができることを知っています:

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

または古き良きもの:

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

しかし、私はむしろ単純なfor-inループを使用したいと思います。私は彼らがよりよく見え、より理にかなっていると思います。

よりシンプルでエレガントな方法はありますか?


Pythonでは簡単です。

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

6
配列にはfor ... inを使用しないでください。とにかく、プロパティの値ではなく、プロパティ名を反復処理します。
Felix Kling

1
オブジェクトではなく配列ですよね?それでalert(obj)
ロケットハズマット

回答:


546

for…in値ではなくプロパティ名を反復処理し、不特定の順序で反復します(はい、ES6の後でも)。配列の反復処理には使用しないでください。それらの場合forEach、値とインデックスの両方を指定した関数に渡すES5のメソッドがあります。

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

またはArray.prototype.entries現在のブラウザのバージョン全体でサポートされているES6 :

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

イテラブル一般(a for…ofではなくループを使用する場合for…in)の場合、組み込みのものはありません。

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

デモ

もしあなたが実際に意味を持っているならfor…in–プロパティを列挙する–あなたは追加のカウンターが必要になるでしょう。Object.keys(obj).forEach動作する可能性がありますが、独自のプロパティのみが含まれます。for…inプロトタイプチェーンの任意の場所に列挙可能なプロパティが含まれます。


2
ああ。私は混乱していた。JavaScriptのfor-inはPythonのものと同じだと思いました。説明をありがとう。
hobbes3

1
@quantumpotato:letsはvarブロックスコープのsです。constsは不変です。
Ry-

1
これは詳細な回答でした。ありがとうございます。議論したすべてのことを本当に明確にした
Dheeraj Bhaskar

1
ばかげた質問ですが、%dと%sは実際には何を表しているのでしょうか、それとも、私がなりたい任意の文字になり得ますか?
クレウィス

2
@klewis:%d整数を%sフォーマットし、文字列をフォーマットします。それらはprintfに基づいています。仕様はconsole.spec.whatwg.org/#formatterで進行中です。
Ry-

163

ES6では、for-ofループを使用することをお勧めします。このようにインデックスを取得できます

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

イテレータArray.entries()返すことに注意してください。これにより、for-ofループで機能することができます。これをObject.entries()と混同しないでください。、キーと値のペアの配列を返す。


9
これは受け入れられたものよりもはるかに良い答えです!
trusktr 2017年

3
この解決策はforEachの解決策よりも優れていると思います...これは、公称のfor ... ofループ構文を使用するため、別の関数を使用する必要はありません。つまり、構文的に優れています。OPはこれを望んでいたようです。
u8y7541 2017

1
entries()空のオブジェクトを返しています:{}。なぜそうなるのでしょうか?私arrayはオブジェクトの配列です。
Joshua Pinter 2017

@JoshuaPinterのObject.entries(array)代わりに試してくださいarray.entries()
tonyg

2
Joshua-オブジェクトはイテレータであり、next()呼び出されるたびに配列内の後続のエントリを返すメソッドを持つオブジェクトです。その中に(目に見える)データはありません。を呼び出すことで、基礎となるオブジェクトのデータを取得しますnext()。cc @tonyg
Shog9

26

これはどう

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

ここで、array.forEachこの方法は有するindexアレイで処理されている現在の要素のインデックスであるパラメータ。


1
ここでのベストアンサー
codepleb

4
選択された回答は、この回答の6年前に投稿されたもので、すでに同じものが含まれています...
Deiv

Foreachはbreak利用できないため、最適化には適していません。
smartworld-dm

20

小さな配列コレクションのソリューション:

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

arr -ARRAY、 obj-現在の要素のキー、 i -COUNTER / INDEX

注:メソッドkeys()はIEバージョン<9では使用できません。Polyfillコードを使用する必要があります。 https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/keys


7
代わりにカウンタを使用し、ループでインクリメントしてください。
mayankcpdixit

2
mayankcpdixitに追加して、代わりにカウンターを使用してください。indexOfはパフォーマンスに悪影響を及ぼす可能性があるためです。
Dean Liu、

1
オブジェクトが大きいほど、これは遅くなります。これはスケーリングしません。
weltschmerz

2
これは無意味に遅くて複雑でvar i = 0;あり、i++;短くて効率的です。さらに、独自のプロパティではない列挙可能なプロパティに対しては機能しません。
Ry-

1
@trusktr:必要な場合は、これを使用しないでください。コレクションを変更するときは、カウンターを変更するだけです。インプレースである必要がない場合は、代わりに優れた関数変換を行ってください。
Ry-

13

for-in-loopsは、オブジェクトのプロパティを反復処理します。動作する場合でも、配列に使用しないでください。

その場合、オブジェクトのプロパティにはインデックスがなく、それらはすべて等しく、決まった順序で実行する必要はありません。プロパティをカウントする場合は、追加のカウンターをセットアップする必要があります(最初の例で行ったように)。

配列のループ:

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

オブジェクトのループ:

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

3
(var i=0; i<a.length; i++)無駄なリソースとして決してしないでください。使用(var i=0, var len = a.length; i<len; i++)
フェリックス・サンス

16
@FelixSanz:廃棄物資源?ありえない。これは、ほとんど必要のない時期尚早なマイクロ最適化であり、var i=0; i<a.length; i++)とにかくすべてのまともなJavaScriptエンジンによって最適化される標準のループパターンです。
ベルギ2014

3
@FelixSanz:はい、そしてvar i=0; i<a.length; i++ベストプラクティスです。
ベルギ2014

1
キス。これが本当に必要なループを作成する場合は、何か間違っているか、または「ベストプラクティス」よりもその必要性についてより良い議論があるかのどちらかです。はい、これは標準的な方法ですが、一般的なパフォーマンスの最適化ではなく、ミクロの最適化のみです。
Bergi、2014

3
KISSはどこにでも適用されます。時期尚早の最適化は反慣行です。
Bergi

7

他の人が言ったように、配列を反復するためにfor..inを使うべきではありません。

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

より簡潔な構文が必要な場合は、forEachを使用できます。

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

この方法を使用する場合は、ES5シムを含めて、古いブラウザーのサポートを追加してください。



1

ここに関数があります eachWithIndexイテラブルでです。

eachWithKeyを使用してオブジェクトと連携する同様の関数を作成することもでき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)

ジェネレータの良いところは、それらが遅延していて、別のジェネレータの結果を引数として取ることができるということです。


1

これは、インデックスと、渡されたジェネレーター関数の値を(遅い)素数検索の例で生成する複合イテレーターの私のバージョンです。

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
}


なぜ関数eachWithIndex[Symbol.iterator]だけでなく関数があるのeachWithIndexですか?eachWithIndexは、反復可能なインターフェースを満たしていませんSymbol.iterator
Ry-

@ Ry-良いキャッチ、eachWithIndex反復可能を受け入れて、閉じた複合反復可能を返すように変更されました。
akurtser

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.