completefrontend
Open lab My progress
THE JAVASCRIPT FIELD GUIDE

Iterating arrays & array-likes

Choose the right loop for arrays, use keys, values and entries, convert array-likes with Array.from, and work safely with arguments and NodeList objects.

By the end, you can
  • 01
    Pick the right loopSay which of for, for...of, and forEach can skip an item, stop early, or give you the index.
  • 02
    Get indexes, values, or bothUse keys() for indexes, values() for items, and entries() for both together.
  • 03
    Work with array-like valuesCopy arguments, strings, array-like objects, and browser NodeLists into real arrays, or borrow one array method.

Pick the right way to go through a list

An array is a list of items in order. Going through the items one by one is called iterating. You do it all the time, but not always for the same reason:

  • Sometimes you need each item's position, its index.
  • Sometimes you need to stop as soon as you find a problem.
  • Sometimes you want to do one thing with every item, like print it.

JavaScript has a different loop for each of these jobs. This lesson shows you which one to pick. It also shows you values that look like arrays but are not real arrays, and how to turn them into real ones.

It builds on these earlier lessons: Loops: while & for, for…of & for…in, Default & rest parameters, and Symbols. The exact rules for how loops get their values come in a later lesson. Here you use the parts you need today.

Two words to know

Iterating an array means visiting its items one at a time, in order. An array-like value has numbered slots (0, 1, 2, …) and a length, but it may not have array methods such as map or filter.

Real-life analogyThree ways to check a row of lockers

Picture a long row of school lockers. If you walk along them yourself with a counter, you can count by twos, walk backward, or stop at locker 12. If a guide walks with you, the guide hands you what is in each locker, in order. You can still say "stop" at any time.

A delivery driver works differently. The driver has a list and visits every house on it. The driver can skip one parcel, but can't end the whole trip from one doorstep.

In real life: You walk along the lockers and count them yourself
In JavaScript: A for loop: you control the index, the direction, and when to stop
In real life: A guide hands you what is in each locker, in order
In JavaScript: A for...of loop: you get each value in order, and break can still stop it
In real life: A delivery driver visits every house on the list
In JavaScript: forEach: your function runs for each item, and you can't stop it early

Here is the plan for the lesson:

  1. Do the same job with three different loops.
  2. Use three small helpers that give you indexes, values, or both.
  3. Turn array-like values into real arrays.
  4. Borrow an array method for one call.
  5. Look at a real NodeList on this page.

for, for…of, and forEach on the same job

STEP THROUGH

The job is small: add up the scores until a negative number appears, and skip any zero. The scores are [3, 0, 4, -2, 5]. The right answer is 7. That's 3 + 4. The 5 comes after -2, so it should never be added.

Two keywords control a loop from the inside:

  • continue skips the rest of this turn and goes to the next item.
  • break ends the whole loop.

forEach is an array method. You give it a function, and it calls that function once for each item. A function you hand to another function like this is called a callback. Inside a callback, return ends only that one call.

Add up the scores until a negative number
Step 0 of 11Ready
Your turn: follow the blue line

The same scores, three loops. Guess whether the 5 after -2 gets added, then step through the real run.

Running in
  1. script
Next: line 1
let total = 0;for (let index = 0; index < scores.length; index += 1) {  const score = scores[index];  if (score === 0) continue;  if (score < 0) break;  total += score;}console.log(total);
CallStoreChangeResultBlue line: runs next. Ran: already executed.
Recent returnsNothing yet. Start with the blue line.
Pick a loop. Does the 5 after -2 get added?

for and for...of can use break to end the loop. In forEach, return ends only the current callback call.

A guided replay recorded from real JavaScript calls, not an engine debugger. Step follows executed statements; Back reviews a snapshot. Reset starts a fresh run.
Two rows of the scores 3, 0, 4, -2, 5. Top row, for...of: the arrow stops at -2 because break ends the loop, so 5 is never read and the total is 7. Bottom row, forEach: the arrow goes all the way to 5, because return skips only -2, so 5 is still added and the total is 12.for...of304-25breaknot readtotal 7forEach304-25returnstill addedtotal 12
The arrow shows how far each loop walks. In for...of, break ends the whole loop at -2. A for loop stops the same way. In forEach, return skips only -2, so the walk goes on to 5.

No loop is always the best. Each one gives you something different:

  • for gives you a counter, which is the index. Use it when the index matters most, when you want to skip or go backward, or when you want full control.
  • for...of gives you each value. Use it when you care about the values and may need break or continue.
  • forEach calls your callback for each item. Use it when you want to do something with every item, such as printing it. Don't use it when you may need to stop early.

The table compares the loops. Read each row as: "if I use this loop, can it stop, does it give me the index, and what is it good for?" A hole, in the fourth column, is an empty slot in an array. You'll meet holes in the next section.

Four loops compared
LoopCan it stop early?Does it give the index?What happens at a hole?Use it when
forYes, with break.Yes. The counter is the index.It reads every index. A hole reads as undefined.You want full control: where to start, where to stop, skipping, or going backward.
for...ofYes, with break.No. Use entries() to get it.It visits a hole and gets undefined.You want each value in order and may need to stop early.
forEachNo. return skips only one item.Yes. It is the second input to your callback.It skips holes.You want to do the same thing with every item.
for...inYes, with break.It gives property names as text, like "0".It skips holes, but it also shows extra properties you added.You loop over an object's properties, not an array.

The last row is for...in. It is easy to mix up with for...of, so it is here as a warning. On an array, for...in gives you the property names as text, like "0" and "1". It also gives you any extra properties you added to the array. Use for...in for objects. For arrays, use one of the first three.

forEach has the same problem with work that finishes later. Code that finishes later is called async. An async function gives back a promise: an object that says "the result will be ready later". forEach doesn't wait for those promises. It finishes straight away and gives back undefined.

forEach doesn't wait for async callbacksPop out in the code editor (opens in a new tab)JavaScript
const orders = ["tea", "toast"];
orders.forEach(async (order) => {
  await Promise.resolve();
  console.log("done " + order);
});
console.log("forEach returned");

Line 6 prints forEach returned first. Only after that does each callback continue past await Promise.resolve() on line 3. Then line 4 prints done tea and done toast.

If the order matters, use for...of with await inside it. If the order doesn't matter, collect the promises in an array and wait for all of them together.

Changing the array while you loop over it is another trap. When you remove an item, every item after it moves one place to the left. But the for loop still moves its index one place to the right. So it can jump over an item.

Removing items while counting skips onePop out in the code editor (opens in a new tab)JavaScript
const cart = ["tea", "milk", "milk", "bread"];
for (let index = 0; index < cart.length; index += 1) {
  if (cart[index] === "milk") cart.splice(index, 1);
}
console.log(cart.join(","));

splice(index, 1) removes one item at index. Line 3 removes the first milk at index 1. The second milk moves into index 1. Then the loop moves on to index 2, so it never checks the second milk. Line 5 prints tea,milk,bread.

If you want to remove items, the later map, filter & reduce lesson shows a clearer way: filter builds a new array and leaves the old one alone.

keys(), values(), and entries()

STEP THROUGH

Arrays have three small helper methods:

  • keys() gives the indexes.
  • values() gives the items.
  • entries() gives pairs: [index, value].

They don't give you an array. Each one gives you an iterator: an object that hands out one result at a time to a loop. To see all the results at once, copy them into an array. You can spread them with ... inside [ ], or use Array.from.

Three helpers on the array ["Ada", "Ben", "Cy"]. keys() gives the indexes 0, 1, 2. values() gives the items "Ada", "Ben", "Cy". entries() gives pairs: [0, "Ada"], [1, "Ben"], [2, "Cy"].["Ada", "Ben", "Cy"]keys()012values()"Ada""Ben""Cy"entries()[0, "Ada"][1, "Ben"][2, "Cy"]
Each row is what one helper hands out, one at a time. keys() gives the positions. values() gives the items. entries() gives both together, as a small [index, value] pair.
Indexes, values, or both
Step 0 of 9Ready
Your turn: follow the blue line

Step through the three helpers: keys() for indexes, values() for items, entries() for both.

Running in
  1. script
Next: line 1
console.log([...queue.keys()].join(","));console.log([...queue.values()].join("|"));for (const [index, name] of queue.entries()) {  console.log(index + ":" + name);}
CallStoreChangeResultBlue line: runs next. Ran: already executed.
Recent returnsNothing yet. Start with the blue line.
A guided replay recorded from real JavaScript calls, not an engine debugger. Step follows executed statements; Back reviews a snapshot. Reset starts a fresh run.

You saw entries() in the for…of lesson. This pattern is worth keeping:

Get the index and the value togetherPop out in the code editor (opens in a new tab)JavaScript
const names = ["Ada", "Ben"];
for (const [index, name] of names.entries()) {
  console.log(index, name);
}

Line 2 gets one pair at a time. The [index, name] part splits each pair into two variables. This is called destructuring. Line 3 prints 0 Ada, then 1 Ben. You don't have to write names[index], and you can still use break if you need it.

Now an unusual case. An array can have an empty slot, called a hole. In ["hat", , "coat"], the two commas with nothing between them leave index 1 empty. An array with holes is called a sparse array.

A hole: for...of visits it, forEach skips it
Step 0 of 11Ready
Your turn: follow the blue line

Holes are rare, but they explain some strange output. Step through to see that for...of visits the hole and forEach skips it.

Running in
  1. script
Next: line 1
for (const item of lockers) {  console.log(item);}lockers.forEach((item) => {  console.log(item);});
CallStoreChangeResultBlue line: runs next. Ran: already executed.
Recent returnsNothing yet. Start with the blue line.
A guided replay recorded from real JavaScript calls, not an engine debugger. Step follows executed statements; Back reviews a snapshot. Reset starts a fresh run.

You won't see holes often, but they explain some strange output. for...of visits every position. At the hole it gets undefined. Most array methods that take a callback, like forEach, skip holes. Their callback never runs for that slot.

Array-like values and Array.from

INTERACTIVE

An array-like value has numbered slots and a length. It looks like an array, but it doesn't get array methods. For example, { 0: "a", 1: "b", length: 2 } is a plain object that is array-like. Array.isArray checks for a real array, and for this object it gives false.

Real-life analogyCopying notes onto a real shopping list

You have notes stuck on the fridge in spots 1 and 2. You know where each one is, but you can't sort them or cross them off. So you copy each note into a shopping list app. Now the list can do all the things the app knows how to do.

In real life: Notes stuck in numbered places on the fridge
In JavaScript: An array-like: slots 0, 1, and a length
In real life: A shopping list app that can sort and cross off
In JavaScript: Array methods such as map, filter, join, and slice
In real life: Copy each note into the app
In JavaScript: Array.from(arrayLike) gives back a real array
Left: a cart called row holds the cards 0: "a" and 1: "b", and has length 2. It is array-like, so it has no map. An arrow marked Array.from leads to a real array card, ["a", "b"], which has map.0: "a"1: "b"rowlength: 2no mapArray.from["a", "b"]has map
row has numbered slots and a length, but it is a plain object, so it has no map. Array.from reads slots 0 and 1 and copies them into a new, real array.
Copy an array-like into a real array
Step 0 of 7Ready
Your turn: follow the blue line

An array-like value looks like an array but has no array methods. Step through copying it, then borrowing a method.

Running in
  1. script
Next: line 1
console.log(Array.isArray(row));try {  row.map((letter) => letter.toUpperCase());} catch (error) {  console.log(error.name);}const real = Array.from(row);console.log(real.map((letter) => letter.toUpperCase()).join(""));console.log(Array.prototype.map.call(row, (letter) => letter + "!").join("|"));console.log(Array.prototype.join.call("abc", "-"));
CallStoreChangeResultBlue line: runs next. Ran: already executed.
Recent returnsNothing yet. Start with the blue line.
A guided replay recorded from real JavaScript calls, not an engine debugger. Step follows executed statements; Back reviews a snapshot. Reset starts a fresh run.

Array.from accepts two kinds of values:

  • Array-like values, with numbered slots and a length.
  • Iterable values: values that for...of can loop over.

A string is both. It has numbered characters and a length, and for...of works on it. A Set is iterable but not array-like. It has no numbered slots and no length.

Array, array-like, or neither?
  • ["a", "b"]
  • the arguments object inside a regular function
  • "abc"
  • { 0: "a", 1: "b", length: 2 }
  • document.querySelectorAll(section)
  • new Set([a, b])
  • { name: "Ada" }
Try it yourself
0 of 7 correct

Sort each value. First ask: is it a real array? If not, does it have numbered slots and a length?

Choose a category for every card. You can change an answer at any time; Reset clears them all.

Borrow one array method for an array-like

PRACTICAL

Sometimes you need just one array method, one time. You don't need to keep a new array. Then you can borrow the method. All array methods live on Array.prototype, the shared object every array gets its methods from.

You borrow a method with .call(). It runs a function and lets you choose what this is inside it. So Array.prototype.map.call(row, fn) runs map with row as this. map then reads the slots of row. The later call, apply & bind lesson explains .call() in full.

Borrow map and joinPop out in the code editor (opens in a new tab)JavaScript
const row = { 0: "a", 1: "b", length: 2 };
const excited = Array.prototype.map.call(row, (letter) => letter + "!");
console.log(excited.join("|"));

console.log(Array.prototype.join.call("abc", "-"));

Line 2 borrows map and adds ! to each letter. Line 3 prints a!|b!. Line 5 borrows join for the string "abc" and prints a-b-c.

Borrowing works, but copying first is usually easier to read: Array.from(row).map(fn). Here is how to choose:

  • Borrow when you need one array method once, for one array-like value.
  • Copy with Array.from when you will use several array methods or pass the array on.
Pick the code that is easy to read

If a teammate has to stop and think about Array.prototype.map.call, then Array.from(row).map is the better choice. The small speed difference almost never matters in a normal app.

Two array-likes you will meet: arguments and NodeList

LIVE BROWSER

Two array-like values show up often in real code:

  • arguments, which holds the values passed into a regular function.
  • A NodeList, which the browser gives back from methods such as querySelectorAll.

You met arguments in Default & rest parameters. When you write the function yourself, use a rest parameter instead. It gives you a real array straight away.

arguments is array-like, but not an arrayPop out in the code editor (opens in a new tab)JavaScript
function list() {
  console.log(Array.isArray(arguments));
  console.log([...arguments].join("|"));
  console.log(Array.from(arguments).map((value) => value * 2).join(","));
}
list(2, 4, 6);

Line 2 prints false: arguments is not a real array, so it has no map. Line 3 spreads it into a new array with [...arguments], which works in today's browsers. Line 4 copies it with Array.from and then uses map. A rest parameter is simpler: function list(...values) { ... } gives you a real array called values.

A web page is made of parts, such as headings, buttons, and sections. The browser lets your code reach these parts through the DOM, short for Document Object Model. A later lesson, Walking & searching the DOM, covers it in full. For now, one fact matters: querySelectorAll gives back a NodeList, not an array.

A browser window, this page, with three section elements. An arrow marked querySelectorAll leads to a card called NodeList. The NodeList has forEach but has no map.this page<section><section><section>querySelectorAllNodeListhas forEachno map
querySelectorAll collects the matching parts of the page into a NodeList. It has numbered slots, a length, and forEach, but no map.
Try this in the console on any pagePop out in the code editor (opens in a new tab)JavaScript
const sections = document.querySelectorAll("section");
console.log(sections.length);
console.log(typeof sections.forEach);
console.log(typeof sections.map);
const headings = Array.from(sections, (section) => section.id);
console.log(headings.join(", "));
Look at a real NodeList from this page
The same code, run on this pagePop out in the code editor (opens in a new tab)JavaScript
const sections = document.querySelectorAll("section");console.log(sections.length);console.log(typeof sections.forEach);console.log(typeof sections.map);const headings = Array.from(sections, (section) => section.id);console.log(headings.join(", "));
What the NodeList hasready
sections.length
0
typeof sections.forEach
not checked yet
typeof sections.map
not checked yet
section ids
Press the button to read this page.
Try it yourself

Press the button. Your browser will collect every section on this page.

The number may change if the page changes. What matters is the shape: numbered slots, a length, usually forEach, and no map.

In most browsers today, a NodeList has forEach but not map. That's why real code often copies it first: Array.from(document.querySelectorAll("button")). After that, you can use map, filter, or sort.

Where you will use this

You pick a loop every time you work with a list of data in an app. Some common cases:

  • Checking form fields until the first error: use for...of, because it can stop with break.
  • Showing which field has the error: use entries(), so you have the index as well as the value.
  • Working with a list the browser gives you: use Array.from to get a real array.
Stop at the first empty form fieldPop out in the code editor (opens in a new tab)JavaScript
const fields = ["name", "email", "password"];
const values = ["Ada", "", "secret"];

for (const [index, value] of values.entries()) {
  if (value === "") {
    console.log(fields[index] + " is required");
    break;
  }
}

Line 4 goes through the values with their indexes. Line 5 checks for an empty value. The email is empty, so line 6 uses its index to print email is required. Line 7 stops the loop, so you see only the first error.

Common mistakes

  • "return stops forEach." It ends only the current callback call. forEach still calls the callback for the rest of the items.
  • "Array-like means array." Array-like only means numbered slots and a length. Array.isArray can still give false.
  • "A NodeList has every array method because it has forEach." Most browsers give it forEach. Methods such as map, filter, and reduce are only on real arrays.
  • "for...in is the index loop for arrays." It loops over property names. It gives them as text, and it also shows extra properties you added.
  • "A hole is the same as an undefined value." for...of visits a hole and gets undefined. forEach skips it.
  • "Borrowing is always better than copying." Borrowing is short but harder to read. Array.from is often clearer, especially before you use several array methods.

Practice exercises

5 EXERCISES
Exercise 1 · Warm-upPredict what return does inside forEach

Read the code. Type the exact number it prints.

Starter codePop out in the code editor (opens in a new tab)JavaScript
const scores = [3, 0, 4, -2, 5];
let total = 0;
scores.forEach((score) => {
  if (score === 0) return;
  if (score < 0) return;
  total += score;
});
console.log(total);

Answer, then press Check. Spacing and letter case don’t matter.

    Exercise 2 · PracticeCopy an array-like and use map

    Run the code, or work it out in your head. It copies the row into an array, makes each letter a capital, and prints the letters joined together. What does it print?

    Starter codePop out in the code editor (opens in a new tab)JavaScript
    const row = { 0: "x", 1: "y", length: 2 };
    const result = Array.from(row).map((letter) => letter.toUpperCase());
    console.log(result.join(""));

    Answer, then press Check. Spacing and letter case don’t matter.

      Exercise 3 · PracticeGet the index and value with entries()

      What is the second line this loop prints?

      Starter codePop out in the code editor (opens in a new tab)JavaScript
      const pets = ["cat", "dog"];
      for (const [index, pet] of pets.entries()) {
        console.log(index + ":" + pet);
      }

      Answer, then press Check. Spacing and letter case don’t matter.

        Exercise 4 · PracticeCheck a real NodeList on this page

        Open the browser console and run the NodeList code from this lesson. Which array method does the NodeList not have?

        Answer, then press Check. Spacing and letter case don’t matter.

          Exercise 5 · ChallengeBorrow join from arrays

          What does this borrowed method call print?

          Starter codePop out in the code editor (opens in a new tab)JavaScript
          console.log(Array.prototype.join.call("abc", "-"));

          Answer, then press Check. Spacing and letter case don’t matter.

            Check your understanding

            8 QUESTIONS
            Iterating arrays quiz · 8 questionsScore: first tries count
            1. Question 1 of 8You need to stop as soon as a negative number appears. Which loop should you pick?

              Choose an answer to see the explanation.

            2. Question 2 of 8What does this for...of program print?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              const values = [1, 0, 2, -1, 9];
              let total = 0;
              for (const value of values) {
                if (value === 0) continue;
                if (value < 0) break;
                total += value;
              }
              console.log(total);

              Choose an answer to see the explanation.

            3. Question 3 of 8This array has a hole. How many times does the forEach callback run?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              const sparse = ["a", , "b"];
              let count = 0;
              sparse.forEach(() => {
                count += 1;
              });
              console.log(count);

              Choose an answer to see the explanation.

            4. Question 4 of 8With const names = ["Ada"], which code gives the first result [0, "Ada"]? (.next().value asks an iterator for its next result.)

              Choose an answer to see the explanation.

            5. Question 5 of 8What does Array.from(row).join("-") print?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              const row = { 0: "a", 1: "b", length: 2 };
              console.log(Array.from(row).join("-"));

              Choose an answer to see the explanation.

            6. Question 6 of 8What does the borrowed join print for the string "abc"?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              console.log(Array.prototype.join.call("abc", "-"));

              Choose an answer to see the explanation.

            7. Question 7 of 8Inside a regular function in today's JavaScript, what is true about arguments?

              Choose an answer to see the explanation.

            8. Question 8 of 8A browser NodeList from querySelectorAll usually has which of these?

              Choose an answer to see the explanation.

            Key takeaways

            • Use for when you need control over the index.
            • Use for...of to go through values in a way that is easy to read. It can still break or continue.
            • Use forEach to do something with every item. It can't stop early, and it doesn't build a new array.
            • keys(), values(), and entries() give iterators. Spread them or use Array.from to see all the results.
            • Array-like values have numbered slots and a length. Copy them with Array.from when you want array methods.

            Iterating means picking the right way to visit each item. For an array-like value, either copy it into a real array or borrow one method.

            Up next: Destructuring arrays & objects.

            CompleteFrontend Clear concepts. Working examples.