รับ loop counter / index ใช้สำหรับ ... ของไวยากรณ์ใน JavaScript


317

ข้อควรระวัง:

คำถามยังคงใช้กับfor…ofลูป> อย่าใช้for…inเพื่อวนซ้ำในArrayใช้เพื่อย้ำคุณสมบัติของวัตถุ ที่กล่าวมานี้


ฉันเข้าใจว่าfor…inไวยากรณ์พื้นฐานใน JavaScript มีลักษณะดังนี้:

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
อย่าใช้สำหรับ ... ในสำหรับอาร์เรย์ และยังไงก็ตามมันซ้ำชื่อคุณสมบัติไม่ใช่ค่าของคุณสมบัติ
เฟลิกซ์คลิง

1
มันเป็นอาร์เรย์ไม่ใช่วัตถุใช่มั้ย ดังนั้นalert(obj)?
จรวดแฮซแมท

คำตอบ:


545

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

หรือ ES6's Array.prototype.entriesซึ่งตอนนี้รองรับเวอร์ชันเบราว์เซอร์ปัจจุบัน:

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ขอบเขตบล็อก consts ไม่เปลี่ยนแปลง
Ry-

1
นี่เป็นคำตอบโดยละเอียดขอบคุณสำหรับมัน ชี้แจงทุกสิ่งที่กล่าวถึงจริงๆ
Dheeraj Bhaskar

1
คำถามงี่เง่า แต่% d และ% s แท้จริงหมายถึงอะไรหรือพวกเขาอาจเป็นจดหมายใด ๆ ที่ฉันต้องการให้พวกเขาเป็น
klewis

2
@klewis: จัด%dรูปแบบจำนวนเต็มและจัด%sรูปแบบสตริง พวกเขากำลังอยู่บนพื้นฐานของprintf สเป็คอยู่ในความคืบหน้าในconsole.spec.whatwg.org/#formatter
Ry-

163

ใน ES6 เป็นการดีที่จะใช้สำหรับ - ของลูป คุณสามารถรับดัชนีสำหรับเช่นนี้

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

โปรดทราบว่าArray.entries()ส่งคืนตัววนซ้ำซึ่งเป็นสิ่งที่อนุญาตให้ทำงานใน for-of loop อย่าสับสนกับสิ่งนี้ Object.entries ()ซึ่งส่งกลับอาร์เรย์ของคู่ของคีย์ - ค่า


9
นี่เป็นคำตอบที่ดีกว่าคำตอบที่ได้รับการยอมรับ!
trusktr

3
ฉันคิดว่าวิธีนี้ดีกว่า foreach แต่ละตัว ... มันใช้ nomral สำหรับ ... ของ syntax ของ loop และคุณไม่ต้องใช้ฟังก์ชั่นแยกต่างหาก กล่าวอีกนัยหนึ่งมันเป็นเรื่องที่ดีกว่า OP ดูเหมือนจะต้องการสิ่งนี้
u8y7541

1
entries(){}คือกลับวัตถุว่างเปล่า: ความคิดใดที่จะเป็นอย่างไร My arrayArray of Objects
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 ปีก่อนหน้านี้และมีสิ่งเดียวกันอยู่แล้วในมัน ...
22425

Foreach ไม่ดีสำหรับการปรับให้เหมาะสมเนื่องจากbreakไม่พร้อมใช้งาน
smartworld-dm

19

โซลูชันสำหรับคอลเลกชันอาร์เรย์ขนาดเล็ก:

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

arr - ARRAY, obj - KEY ขององค์ประกอบปัจจุบัน, i - COUNTER / INDEX

ประกาศ: ปุ่มวิธี()ไม่สามารถใช้ได้กับ 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
KISS หากคุณเขียนลูปที่คุณต้องการสิ่งนี้จริงๆคุณก็กำลังทำอะไรผิดพลาดหรือคุณมีข้อโต้แย้งที่ดีกว่าสำหรับความจำเป็นมากกว่า "วิธีปฏิบัติที่ดีที่สุด" ใช่มันเป็นมาตรฐานปฏิบัติ แต่ไม่ใช่สำหรับการเพิ่มประสิทธิภาพทั่วไป แต่สำหรับ micro-optimization เท่านั้น
Bergi

3
จูบใช้ทุกที่ การเพิ่มประสิทธิภาพก่อนวัยอันควรเป็นการต่อต้านการปฏิบัติ
Bergi

7

อย่างที่คนอื่น ๆ พูดคุณไม่ควรใช้สำหรับในการทำซ้ำในอาเรย์

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ที่ใช้งานได้กับทุกสิ่งที่ iterable

นอกจากนี้คุณยังสามารถเขียนฟังก์ชั่นที่คล้ายกันeachWithKeyที่ทำงานร่วมกับ objets 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

นั่นคือเวอร์ชันของฉันของ iterator แบบคอมโพสิตที่ให้ค่าดัชนีและค่าของฟังก์ชันตัวกำเนิดที่ส่งผ่านด้วยตัวอย่างของการค้นหาเฉพาะ (ช้า):

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ไม่พอใจอินเตอร์เฟซ iterable Symbol.iteratorซึ่งเป็นจุดรวมของ
Ry-

@ Ry- จับได้ดีเปลี่ยนeachWithIndexเป็นยอมรับ iterable และส่งคืนคอมโพสิตที่ปิดได้ iterable
akurtser

1

ด้านบนของคำตอบที่ทุกคนดีมากโพสต์ที่ฉันต้องการที่จะเพิ่มที่การแก้ปัญหามากที่สุดคือ performant entriesES6 ดูเหมือนว่าจะเป็นเรื่องง่ายสำหรับผู้พัฒนาหลายคนที่นี่ดังนั้นฉันจึงสร้างbenchamrk ที่สมบูรณ์แบบนี้

ป้อนคำอธิบายรูปภาพที่นี่

เร็วกว่าถึง 6 เท่า สาเหตุหลักเนื่องจากไม่จำเป็นต้อง: a) เข้าถึงอาร์เรย์มากกว่าหนึ่งครั้งและ b) ส่งดัชนี

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.