A Simple Introduction to JavaScript Iterators
Tags: javascript, webdev, tutorial, beginners
Published: 2021-11-01 06:48:07

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.

```JavaScript
// 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 }
```

## 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.

```JavaScript
// 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'
```

### Arrays

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

```JavaScript
// 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'
```

### Maps

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

```JavaScript
// 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'
```

### 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.

```JavaScript
// 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'
```

## 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.

```JavaScript
// 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'
```

### 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.

```JavaScript
// 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 ]
```

### 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.

```JavaScript
// 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' ]
```

### 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.

```JavaScript
// 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' ]
```

## 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()`.

[iterator]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Iterators_and_Generators#iterables
[symbol.iterator]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/iterator
[map method]: https://blog.alexdevero.com/es6-es7-es8-modern-javascript-pt4/
[for loop]: https://blog.alexdevero.com/javascript-loops/#for-loop
[map]: https://blog.alexdevero.com/maps-in-javascript/
[set]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set
[destructuring assignment]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment