DEV Community

Cover image for JavaScript: Iterators and Generators (Part-1)πŸ’‘πŸ“Œ
Majedur Rahman
Majedur Rahman

Posted on

JavaScript: Iterators and Generators (Part-1)πŸ’‘πŸ“Œ

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 forloops 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. Interface Iterable 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.

Iterator

Principle of an Iterator

  1. next() : next function returns an object consisting of two keys {value , done}.
  2. {value , done} : Each next execution returns a value but done is returned as false until the iteration is complete(reaches length of the array/ object). Once all the values are iterated the done is returned as true and value turns out to be undefined.
  3. 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.

Iterator Object

To use an iterator you typically call the Symbol.iterator method on a collection or data structure to obtain an iterator object.

Iterator example

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.

Iterator Protocol

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.

Iterator Protocol1

Key points about Symbol.iterator :

  • value of this is a function.
  • when we call the function associated with Symbol.iterator , it gives us a iterator 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 the done status.

Itr example

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"
}
Enter fullscreen mode Exit fullscreen mode

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 via Symbol.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
    }
}
Enter fullscreen mode Exit fullscreen mode

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.

Iterable protocol

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
}
Enter fullscreen mode Exit fullscreen mode

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.

Top comments (3)