DEV Community 👩‍💻👨‍💻

DEV Community 👩‍💻👨‍💻 is a community of 963,673 amazing developers

We're a place where coders share, stay up-to-date and grow their careers.

Create account Log in
Cover image for A Simple Introduction to JavaScript Iterators
Alex Devero
Alex Devero

Posted on • Originally published at blog.alexdevero.com

A Simple Introduction to JavaScript Iterators

Iterators are data structures that allow you process a sequence of elements more efficiently. This tutorial will help you learn about what JavaScript iterators are and how to find out if something is an iterator. You will also learn about existing types of iterators, their consumers and how to work with them.

A quick introduction to JavaScript iterators

An iterator is type of a data structure. It is a collection of elements. Two examples of such collections are strings and arrays. The first, string is a collection of characters. The second, array is a collection items. That said, not every collection is an iterator.

For a collection to be an iterator, it has to conform to specification of Iterable interface. This interface says that the collections must implement a method Symbol.iterator. This means that this method has to be available on the collection object. This method when invoked returns an Iterator object.

This Iterator object contains one method called next(). This method returns an object with two properties, value and done. The value property contains item from the collection that is currently in the iteration sequence. The done is a boolean that says if the iteration is at the end.

We can use this next() method to iterate over the collection in a controlled manner. Unlike with for loop or map method where we can't stop and resume the iteration, JavaScript iterators allow us to do this. They allow us to get each item in the collection, to resume the iteration, when we want.

// Create an array:
const list = [1, 3, 5, 7, 9]
// Create iterator for "list" array:
const listIterator = list[Symbol.iterator]()

// Log the iterator object:
console.log(listIterator)
// Output:
// Iterator [Array Iterator] { __proto__: { next: ƒ next() } }

// Try next() method:
listIterator.next()
// Output:
// { value: 1, done: false }

listIterator.next()
// Output:
// { value: 3, done: false }

listIterator.next()
// Output:
// { value: 5, done: false }

listIterator.next()
// Output:
// { value: 7, done: false }

listIterator.next()
// Output:
// { value: 9, done: false }

listIterator.next()
// Output:
// { value: undefined, done: true }
Enter fullscreen mode Exit fullscreen mode

Types of JavaScript iterators

There are currently four data types in JavaScript that are iterable. These types that are iterable are strings, arrays, maps and sets.

Strings

The idea that string may be iterable may sound weird. However, it is true. We can validate this with a simple test. If string is an iterable, it should have the Symbol.iterator method. If we invoke this method, we should get the iterator object. With this object, we should also get the next() method.

// Create a string and iterator object for it:
const str = 'It worked'
const strIterator = str[Symbol.iterator]()

// Iterate over individual characters with next():
strIterator.next()
// Output:
// { value: 'I', done: false }

strIterator.next()
// Output:
// { value: 't', done: false }

strIterator.next()
// Output:
// { value: ' ', done: false }

strIterator.next()
// Output:
// { value: 'w', done: false }


// Iterate over the string using for...of loop:
for (const char of str) {
  console.log(char);
}
// Output:
// 'I'
// 't'
// ' '
// 'w'
// 'o'
// 'r'
// 'k'
// 'e'
// 'd'
Enter fullscreen mode Exit fullscreen mode

Arrays

Arrays are the second type that is iterable. Again, we can test this by using the Symbol.iterator method and for...of loop.

// Create an array and iterator object for it:
const names = ['Josh', 'Howard', 'Lucy', 'Victoria']
const namesIterator = names[Symbol.iterator]()

// Iterate over individual items with next():
namesIterator.next()
// Output:
// { value: 'Josh', done: false }

namesIterator.next()
// Output:
// { value: 'Howard', done: false }

namesIterator.next()
// Output:
// { value: 'Lucy', done: false }

namesIterator.next()
// Output:
// { value: 'Victoria', done: false }


// Iterate over the array using for...of loop:
for (const name of names) {
  console.log(name);
}
// Output:
'Josh'
'Howard'
'Lucy'
'Victoria'
Enter fullscreen mode Exit fullscreen mode

Maps

The third iterable type is the Map object. With Maps, we can iterate over their key and value pairs.

// Create a Map and iterator object for it:
const map = new Map()
map.set('name', 'Tony Stark')
map.set('alias', 'Iron Man')
map.set('reality', 'Earth-616')
map.set('education', 'MIT')

const mapIterator = map[Symbol.iterator]()

// Iterate over individual items with next():
mapIterator.next()
// Output:
// { value: [ 'name', 'Tony Stark' ], done: false }

mapIterator.next()
// Output:
// { value: [ 'alias', 'Iron Man' ], done: false }

mapIterator.next()
// Output:
// { value: [ 'reality', 'Earth-616' ], done: false }

mapIterator.next()
// Output:
// { value: [ 'education', 'MIT' ], done: false }


// Iterate over the Map using for...of loop:
for (const [key, value] of map) {
  console.log(`${key}: ${value}`);
}
// Output:
'name: Tony Stark'
'alias: Iron Man'
'reality: Earth-616'
'education: MIT'
Enter fullscreen mode Exit fullscreen mode

Sets

The fourth and last iterable type is the Set object. Set objects are similar to arrays. The main difference between a Set and an array is that Set doesn't allow duplicate values. When you try to add duplicate value, Set will keep only the first occurrence of the value and ignore the second.

// Create a map and iterator object for it:
const set = new Set(['north', 'east', 'west', 'south'])
const setIterator = set[Symbol.iterator]()

// Iterate over individual items with next():
setIterator.next()
// Output:
// { value: 'north', done: false }

setIterator.next()
// Output:
// { value: 'east', done: false }

setIterator.next()
// Output:
// { value: 'west', done: false }

setIterator.next()
// Output:
// { value: 'south', done: false }


// Iterate over the Set using for...of loop:
for (const item of set) {
  console.log(item);
}
// Output:
'north'
'east'
'west'
'south'
Enter fullscreen mode Exit fullscreen mode

Iterable consumers and working with iterable types

These were the four iterable types we can work with in JavaScript. The next question is, how can we use them, or consume them. There are four popular consumers that allow us to "consume" iterables. These consumers are: for...of loop, destructuring assignment, spread operator and Array.from().

for...of loop

The first way to iterate over JavaScript iterators is by using the for...of loop. The downside of for...of loop is that it doesn't give us much of the control over the iteration. However, if all we need is to retrieve each item in the collection, it will do the job.

// Array:
const numbers = [2, 4, 6]

for (const num of numbers) {
  console.log(num)
}
// Output:
// 2
// 4
// 6


// String:
const word = 'Root'

for (const char of word) {
  console.log(char)
}
// Output:
// 'R'
// 'o'
// 'o'
// 't'


// Map:
const map = new Map([
  ['name', 'Joe'],
  ['age', 33],
])

for (const [key, val] of map) {
  console.log(`${key}: ${val}`)
}
// Output:
// 'name: Joe'
// 'age: 33'


// Set:
const set = new Set(['C++', 'Assembly', 'JavaScript', 'C++'])

for (const language of set) {
  console.log(language)
}
// Output:
// 'C++'
// 'Assembly'
// 'JavaScript'
Enter fullscreen mode Exit fullscreen mode

Destructuring assignment

One quick way to retrieve items from JavaScript iterators is by using destructuring assignment. With destructuring, we can retrieve any item we need, single item at the time or multiple items at once.

// Array:
const genres = ['rock', 'hip hop', 'r&b', 'metal', 'soul']

// Destructuring assignment:
const [ first, second, ...rest ] = genres

console.log(first)
// Output:
// 'rock'

console.log(second)
// Output:
// 'hip hop'

console.log(rest)
// Output:
// [ 'r&b', 'metal', 'soul' ]


// String:
const word = 'Recursion'

// Destructuring assignment:
const [first, second, third, ...rest] = word

console.log(first)
// Output:
// 'R'

console.log(second)
// Output:
// 'e'

console.log(third)
// Output:
// 'c'

console.log(rest)
// Output:
// [ 'u', 'r', 's', 'i', 'o', 'n' ]


// Map:
const map = new Map([
  ['water', 'fire'],
  ['white', 'black'],
  ['left', 'right'],
])

// Destructuring assignment:
const [start, middle, end] = map

console.log(start)
// Output:
// [ 'water', 'fire' ]

console.log(middle)
// Output:
// [ 'white', 'black' ]

console.log(end)
// Output:
// [ 'left', 'right' ]


// Set:
const set = new Set([1, 33, 777, 9999])

// Destructuring assignment:
const [ first, second, ...rest ] = set

console.log(first)
// Output:
// 1

console.log(second)
// Output:
// 33

console.log(rest)
// Output:
// [ 777, 9999 ]
Enter fullscreen mode Exit fullscreen mode

Spread operator

Spread operator offers a quick and simple way to iterate over iterable type and transform it into an array. This will not be useful when working with arrays. It can still be handy when dealing with maps, strings and also sets.

// String:
const word = 'closure'

// Spread:
const wordSpread = [...word]

console.log(wordSpread)
// Output:
// [
//   'c', 'l', 'o',
//   's', 'u', 'r',
//   'e'
// ]


// Map:
const map = new Map([
  ['fruit', 'apple'],
  ['thatGreenThing', 'kale'],
  ['beverage', 'tea']
])

// Spread:
const mapSpread = [...map]

console.log(mapSpread)
// Output:
// [
//   [ 'fruit', 'apple' ],
//   [ 'thatGreenThing', 'kale' ],
//   [ 'beverage', 'tea' ]
// ]


// Set:
const set = new Set(['Halo', 'Quake', 'NFS', 'C&C'])

// Spread:
const setSpread = [...set]

console.log(setSpread)
// Output:
// [ 'Halo', 'Quake', 'NFS', 'C&C' ]
Enter fullscreen mode Exit fullscreen mode

Array.from()

Along with spread operator, Array.from() also allows us to transform any iterable to an array. All we have to do is to pass the iterable as an argument to the from() method.

// String:
const word = 'iterable'

// Spread:
const wordArray = Array.from(word)

console.log(wordArray)
// Output:
// [
//   'i', 't', 'e',
//   'r', 'a', 'b',
//   'l', 'e'
// ]


// Map:
const map = new Map([
  [1, 1],
  [2, 10],
  [3, 11],
  [4, 100]
])

// Spread:
const mapArray = Array.from(map)

console.log(mapArray)
// Output:
// [ [ 1, 1 ], [ 2, 10 ], [ 3, 11 ], [ 4, 100 ] ]


// Set:
const set = new Set(['BTC', 'ETH', 'ADA', 'EOS'])

// Spread:
const setArray = [...set]

console.log(setArray)
// Output:
// [ 'BTC', 'ETH', 'ADA', 'EOS' ]
Enter fullscreen mode Exit fullscreen mode

Conclusion: A simple introduction to JavaScript iterators

Iterators and iterables can be handy when we need collection over which we can iterate in a controlled fashion. In this tutorial, we've looked at what JavaScript iterators are, what types of iterators are available and how work with them, using the for...of loop, destructuring assignment, spread operator and Array.from().

Top comments (1)

Collapse
 
darkwiiplayer profile image
𒊩Wii 💖💛💚💙💜💝💟 • Edited on

I'm still very salty that Svelte doesn't really support iterators in its #each loops; I constantly find myself writing things like {#each [...someSet] as item} when it could be so much simpler :D


Oh and it gets worse when you have methods like Map.prototype.values(), where the extra function call makes it seem even more cluttered: {#each [...someMap.values()] as value}

Need a better mental model for async/await?

Check out this classic DEV post on the subject.

⭐️🎀 JavaScript Visualized: Promises & Async/Await

async await