JavaScript에서 for… of 구문을 사용하여 루프 카운터 / 인덱스 가져 오기


317

주의:

질문은 여전히 for…of루프에 적용됩니다 .> 배열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루프를 사용하고 싶습니다 . 나는 그들이 더 좋아 보이고 더 이해가된다고 생각합니다.

더 단순하거나 더 우아한 방법이 있습니까?


파이썬에서는 쉽습니다 :

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

6
배열에는 for ... in을 사용하지 마십시오. 어쨌든 속성 값이 아닌 속성 이름을 반복합니다.
Felix Kling

1
객체가 아닌 배열입니다. 그래서 alert(obj)?
로켓 Hazmat

답변:


545

for…in값이 아닌 속성 이름을 반복 하며 지정되지 않은 순서로 수행합니다 (예 : ES6 이후에도). 배열을 반복하는 데 사용해서는 안됩니다. 그것들을 위해 ES5의 forEach방법은 값과 색인을 주어진 함수에 전달합니다.

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 's :

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

일반적으로 iterables (의 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입니다. const변경되지 않습니다.
Ry-

1
자세한 답변이었습니다. 감사합니다. 정말 명확하게 모든 것을 논의
Dheeraj 바스 카르에게

1
바보 같은 질문이지만 % d와 % s는 실제로 무엇을 의미합니까, 아니면 내가 원하는 글자가 될 수 있습니까?
klewis

2
@klewis : %d정수를 %s포맷하고 문자열을 포맷합니다. 그들은 printf 기반으로 합니다. console.spec.whatwg.org/#formatter 에서 스펙이 진행 중 입니다.
Ry-

162

ES6에서는 for-of-loop를 사용하는 것이 좋습니다. 이 같은 인덱스를 얻을 수 있습니다

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

for-of 루프에서 작동 할 수 있는 iteratorArray.entries()리턴 합니다 . 키-값 쌍 의 배열 을 반환하는 Object.entries () 와 이것을 혼동하지 마십시오 .


9
이것은 허용되는 것보다 훨씬 더 나은 대답입니다!
trusktr

3
이 솔루션이 forEach 솔루션보다 낫다고 생각합니다 ... 루프 구문에 nomral for ... of를 사용하므로 별도의 함수를 사용할 필요가 없습니다. 다시 말해, 문법적으로 더 좋습니다. OP가 원했던 것 같습니다.
u8y7541

1
entries()빈 객체를 반환합니다 : {}. 왜 그런지 아십니까? 내 array개체의 배열입니다.
Joshua Pinter

@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

19

소규모 어레이 수집을위한 솔루션 :

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

arr -ARRAY, obj- 현재 요소의 키, i- 카운터 / 인덱스

참고 : IE 버전 <9에서는 메소드 키 () 를 사용할 수 없으므로 사용해야합니다. 없으므로 Polyfill 코드를 합니다. https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/keys


7
나는 제안합니다 : 대신 카운터를 사용하고 루프에서 증가시킵니다.
mayankcpdixit

2
mayankcpdixit에 추가하면 indexOf가 성능에 부정적인 영향을 줄 수 있으므로 대신 카운터를 사용하십시오.
Dean Liu

1
물체가 클수록 이것이 느려집니다. 이것은 확장되지 않습니다.
dchacke

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++)
Félix Sanz

16
@FelixSanz : 폐기물 자원? 절대 안돼. 그것은 거의 필요하지 않은 조기 마이크로 최적화이며, var i=0; i<a.length; i++)어쨌든 모든 괜찮은 자바 스크립트 엔진에 의해 최적화되는 표준 루프 패턴입니다.
Bergi

3
@FelixSanz : 그렇습니다. 그리고 var i=0; i<a.length; i++최고의 연습입니다.
Bergi

1
키스 . 당신이 정말로 이것을 필요로하는 루프를 작성한다면, 당신은 무언가 잘못하고 있거나 "최고의 실천"보다 그것의 필요성에 대한 더 나은 주장을 가지고 있습니다. 그렇습니다. 이는 표준 성능이지만 일반적인 성능 최적화는 아니지만 미세 최적화를위한 것입니다.
Bergi

3
KISS는 모든 곳에서 적용됩니다. 조기 최적화 는 예방 조치입니다.
Bergi

7

다른 사람들이 말했듯이 for..in을 사용하여 배열을 반복해서는 안됩니다.

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

더 깨끗한 구문을 원하면 forEach를 사용할 수 있습니다.

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

이 방법을 사용하려면 ES5 shim을 포함시켜 이전 브라우저에 대한 지원을 추가하십시오.


2

rushUp이 제공 한 답변은 정확하지만 더 편리합니다.

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

1

여기 함수가 있습니다 eachWithIndex반복 가능한 모든 것에서 작동 가 있습니다.

eachWithKey사용하여 objet과 작동 하는 유사한 기능 을 작성할 수도 있습니다 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있습니까? eachWithIndexiterable 인터페이스를 만족시키지 않습니다 Symbol.iterator.
Ry-

@ Ry- Good catch, eachWithIndexiterable을 수락하고 닫힌 복합 iterable을 반환하도록 변경되었습니다 .
akurtser

1

모든 사람들이 게시 한 훌륭한 답변 외에도 가장 우수한 솔루션은 ES6이라고 덧붙이고 싶습니다 entries. 여기에 많은 개발자들에게는 금기 사항이있어서 이 있으므로이 perf benchamrk을 .

여기에 이미지 설명을 입력하십시오

~ 6 배 더 빠릅니다. 주로 a) 배열에 두 번 이상 액세스하고 b) 인덱스를 캐스트 할 필요가 없기 때문입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.