JavaScript: Iterators and Generators (Part-1)💡📌
Majedur Rahman
Posted on January 17, 2024
In JavaScript, iterating through data set or collections objects such as arrays
is a very common operation. JavaScript provides a number of ways of iterating over a data-set or collection, from simple for
loops to Array.prototype.map()
etc.
ES6 introduces a new mechanism for traversing data: iteration. These concepts are central to iteration:
- Iterable is a data structure that can be iterated over through iteration.
- Iterator is a pointer to the next element in the iteration.
- Iterability is an event where data consumers get their data from Iterable data sources with the help of Iterator. Practically not possible to create consumers for all data sources, that’s the reason ES6 introduces Iterable interface/protocol.
So Iterators and Generators provide a mechanism for customizing the behavior of control structures or data-consumers such as for...of
loop in JavaScript to iterate over collections in a simple and readable manner. Built-in JavaScript functions and methods, such as Array.prototype.map()
or Array.prototype.forEach()
to process collections internally use Iterators.
In this article we will cover the below concepts :
1. Iterators.
2. Iterators Interface/Protocol.
3. Iterables.
4. Iterables Interface/Protocol.
5. Custom Iterators.
6. Custom Iterables.
7. Iterators vs Iterables.
Iterators.
What is Iterators?
An Iterator is a structured pattern for pulling information from a source in one-at-a-time fashion. Conceptually, an "iterator" is an object that provides a sequence of values one-at-a-time when requested.
Long story short, iterator is a closure which returns an object having function next(), on each subsequent calling of the next function we get the next iteration value.
Principle of an Iterator
- next() :
next
function returns an object consisting of two keys{value , done}
. - {value , done} : Each
next
execution returns avalue
butdone
is returned asfalse
until the iteration is complete(reaches length of the array/ object). Once all thevalues
are iterated thedone
is returned astrue
andvalue
turns out to beundefined
. - Symbol.iterator: This
[Symbol.iterator]
is a well-known symbol in JavaScript that defines the default iterator for an object. It is typically implemented as a function that returns the iterator object itself.
To use an iterator you typically call the Symbol.iterator
method on a collection or data structure to obtain an iterator object.
Iterators Interface/Protocol
The concept of the protocol can be split into two. The iterable, the data structure itself, and the iterator, a pointer that moves over the iterable.
The iterator interface or protocol defines an standard to produce a sequence of values. This protocol
is really an object that contains a next() method and this next() method returns another object with two properties, value and done.
The next() method always returns an object with value and done properties. The value property contains the current value of the iteration and the done property indicates the termination of the iterator.
whenever, any data-source
implement the [Symbol.iterator]
protocol that produces an iterator and makes the data-structure
iterable whenever the data structure in placed in a loop.
Key points about Symbol.iterator
:
- value of this is a function.
- when we call the function associated with
Symbol.iterator
, it gives us aiterator object
. -
iterator object returned from
Symbol.iterator
method has.next
method associated it it. - And one each
next()
call we get the next value along thedone
status.
We have to iterate one extra time, then only we get
done:true
.
Custom Iterators
Now, let’s create an custom iterator that returns only the administrators' names. consider true
values are admin.
const users = {
james: false,
andrew: true,
alexander: false,
daisy: false,
luke: false,
clare: true,
[Symbol.iterator]() {
const keys = Object.keys(this);
let index = 0;
const iterator = {
// each call will return a new iterator
next: () => {
while (!this[keys[index]] && index < keys.length) index++;
// here the relevant part
return {
done: index >= keys.length,
// after reading the name corresponding to the current index,
// do not forget to move forward the 'index'
// for the next iteration
value: keys[index++],
};
}
}
return iterator;
}
}
for (let name of users) {
console.log(name); //"andrew","clare"
}
Iterables.
What is Iterable?
In general, it is a data structure
that allows its data to be consumed. It does so by implementing a method whose key is Symbol.iterator
which returns an iterator.
From a technical perspective, an “iterable” isn’t a specific data type in JS. Rather, it’s a protocol that various data types and objects can implement and the JS engine will treat such values as iterables.
Principle of an Iterable
- To call any object iterable, it needs to implement the iterable interface.
- The iterable object must have a
@@iterator
method that returns an iterator object. The@@iterator
key is a symbol that can be accessed viaSymbol.iterator
. - The property
Symbol.Iterator
on an object converts the object into Iterable. - Iterables which can iterate only once (such as Generators) customarily return
this
from their @@iterator method. - Iterables which can be iterated many times must return a new iterator on each invocation of @@iterator.
let iterable ={
[Symbol.iterator] : () => {
//Iterator defination
}
}
Iterable Interface/Protocol
The iterable protocol allows JavaScript objects to define or customize their iteration behavior. By being an iterable object it needs to implement an @@iterator
method (an iterator key), meaning that the object (or one of the objects up its prototype chain
) must have a property with a @@iterator
key which is available via constant Symbol.iterator
, this method needs to returns an iterator protocol.
Whenever an object needs to be iterated (such as at the beginning of a for...of
loop), its @@iterator
method is called with no arguments, and the returned iterator is used to obtain the values to be iterated.
Custom Iterables.
We are writing an iterable object with will iterate over an array and print the array values.
const iterable = {
numbers: [1, 2, 3, 4, 5],
[Symbol.iterator]: function () {
let index = 0;
return {
next: function () {
if (index < this.numbers.length) {
let temp = { value: this.numbers[index], done: false };
index++;
return temp;
} else {
return { value: undefined, done: true };
}
}.bind(this),
};
},
};
for (let i of iterable) {
console.log(i); // result is: 1,2,3,4,5
}
Iterators vs Iterables.
- As discussed above Iterable is an object which consists of an Iterator
- Iterator is defined under the key Symbol.Iterator.
- Iterator is the action definition of iteration this holds the custom logic of next() function
- Next function is called iteratively and as per the protocol it returns value and done keys.
we will discuss about Generators in upcoming blog JavaScript: Iterators and Generators (Part-2).
"That's all folks!"
References:
Books
ES6 & Beyond (YDKJS)-kyle simpson
Images
Google and designed.
Posted on January 17, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.