Javascript Iterator

Overview

Javascript has a traditional way of iterating on the objects with loops like for, while, which need to keep track of each item in the collection. But this approach creates many complexities when we have to do nested iterations. We need to initialize a variable for each loop and keep track of them.

To solve this ES6 has introduced iterators which makes working with a collection of items easier. Iterators, when called, returns the next object of the collection.

What are javascript iterator?

Iterators are just objects with a set of an interface designed for iteration. Each iterator object has a next() methods that returns an object. Each returned object has two properties value the next value and done a boolean indicating that this is the last item.

The iterator keeps track of the current item in the collection and with each next() call returns the appropriate next value.

function * numbers(){
   yield 1;
   yield 2;
   yield 3;
}

let iterator = numbers();
console.log(iterator.next());
//{value: 1, done: false}

console.log(iterator.next());
//{value: 2, done: false}

console.log(iterator.next());
//{value: 3, done: false}

console.log(iterator.next());
//{value: undefined, done: true}

Here we have used a generator to create the iterator.

What are generators?

A generator is a function that returns an iterator. They are indicated by * and uses yield keyword.

function * createIterator(){
  yield "value";
  yield 2;
  yield object = {};
}

The * before createIterator() makes this function a generator. yield keyword specifies what value the function should return when next() method is called.

The generator function stops the execution after each yield statement. yield can only be used inside the generator functions.

function * createIterator(){
   let friends = ['Pranav', 'Sachin', 'Panam', 'Yogesh', 'Abhilash'];
   for(let i = 0; i < friends.length; i++){
     yield friends[i];
   } 
}

let iterator = createIterator();
console.log(iterator.next());
//{value: 'Pranav', done: false}

console.log(iterator.next());
//{value: 'Sachin', done: false}

console.log(iterator.next());
//{value: 'Panam', done: false}

console.log(iterator.next());
//{value: 'Yogesh', done: false}

Generators are one of the most important features of ES6. As they are just functions they can be used anywhere.

Generator Return Statements

As generators are function we can specify return statement both to exit early and specify a return value for the last call to the next() method

function * createIterator(){
   let friends = ['Pranav', 'Sachin', 'Panam', 'Yogesh', 'Abhilash'];
   for(let i = 0; i < friends.length; i++){
     if(i == 2){
       return 'arun';
     }
     yield friends[i];
   } 
}

let iterator = createIterator();
console.log(iterator.next());
//{value: 'Pranav', done: false}

console.log(iterator.next());
//{value: 'Sachin', done: false}

console.log(iterator.next());
//{value: "arun", done: true}

Passing value to generators

When an argument is passed to the next() method, that argument becomes the value of the yield statement inside a generator.

function *createIterator() {
    let first = yield 1;
    let second = yield first + 5;       
    yield second + 6;                   
}

let iterator = createIterator();

console.log(iterator.next());  
//"{ value: 1, done: false }"

console.log(iterator.next(4)); 
// "{ value: 9, done: false }"

console.log(iterator.next(5));          
// "{ value: 11, done: false }"

console.log(iterator.next());
{value: undefined, done: true}

Generator Objects

Generators can also be added to the objects.

let friends = {
    createIterator: function *(items) {
        for (let i = 0; i < items.length; i++) {
            yield items[i];
        }
    },

    *createIterator2(items){
        for (let i = 0; i < items.length; i++) {
            yield items[i];
        }
    }
};

let iterator = friends.createIterator(['Pranav', 'Sachin', 'Panam', 'Yogesh', 'Abhilash']);

console.log(iterator.next());
//{value: 'Pranav', done: false}

console.log(iterator.next());
//{value: 'Sachin', done: false}

let iterator2 = friends.createIterator2(['goku', 'krillin', 'vegeta']);

console.log(iterator2.next());
//{value: 'goku', done: false}

console.log(iterator2.next());
//{value: 'krillin', done: false}

Iteratable

An Iteratable is an object with a symbol.iterator property. It is closely related to itertors. symbol.iterator specifies a function that returns the iterator for the given object. All collection objects (arrays, sets, and maps) and strings are iterables in ECMAScript6 and so they have a default iterator specified. It is designed to be used with new for-of.

For-of

ES6 has introduced a new loop called for-of which is used to iterate over the values of the object. It can be used on iterators.

Default iterator for the array

let friends = ['Pranav', 'Sachin', 'Panam', 'Yogesh', 'Abhilash'];
let iterator = friends[Symbol.iterator]();

console.log(iterator.next());
//{value: "Pranav", done: false}

console.log(iterator.next());
//{value: "Sachin", done: false}

console.log(iterator.next());
//{value: "Panam", done: false}

A for-of loop calls next() on an iterable each time the loop executes and stores the value from the result object in a variable. The loop continues this process until the returned object’s done property is true.

For-of with Array

let friends = ['Pranav', 'Sachin', 'Panam', 'Yogesh', 'Abhilash'];
for(let friend of friends){
  console.log(friend);
}
//"Pranav"
//"Sachin"
//"Panam"
//"Yogesh"
//"Abhilash"

For-of with objects

We can also use the for-of on the objects. There are different inbuilt methods which can be used to achieve the same.

Built In iterators

ES6 has three types of collection objects: arrays, maps, and sets. All three have the following built-in iterators to help you navigate their content:

  • entries() - Returns an iterator whose values are a key-value pair
  • values() - Returns an iterator whose values are the values of the collection
  • keys() - Returns an iterator whose values are the keys contained in the collection

For-of with plain object

let car = {
  maker: "BMW",
  color: "red",
  year : "2010",
}

for (let prop of Object.keys(car)){
  console.log(car[prop]);
}
// BMW maker
// red color
// 2010 year

For-of with Map

let data = new Map();
data.set("prashant", 1);
data.set("pranav", 1);
data.set("sachin", 1);
data.set("panam", 1);

for (let prop of data.entries()){
  console.log(prop);
}

//["prashant", 1]
//["pranav", 1]
//["sachin", 1]
//["panam", 1]

Destructuring the data

let data = new Map();
data.set("prashant", 1);
data.set("pranav", 2);
data.set("sachin", 3);
data.set("panam", 4);

for (let [key, value] of data.entries()){
  console.log(`${key} at ${value}`);
}

//prashant at 1
//pranav at 2
//sachin at 3
//panam at 4

For-of with Set

let data = new Set();
data.add('prashant');
data.add('pranav');
data.add('sachin');
data.add('panam');

for (let prop of data.values()){
  console.log(prop);
}

//prashant
//pranav
//sachin
//panam

For-of with String

let data = 'prashant';

for (let char of data){
  console.log(char);
}

/*
p
r
a
s
h
a
n
t
*/

For-of with generators

Iterators created by generators are also iterables, as generators assign the symbol.iterator property by default.

let friends = {
    *createIterator(items){
        for (let i = 0; i < items.length; i++) {
            yield items[i];
        }
    }
};

let iterator = friends.createIterator(['Pranav', 'Sachin', 'Panam', 'Yogesh', 'Abhilash']);
for(let friend of iterator){
   console.log(friend);
}

//Pranav
//Sachin
//Panam
//Yogesh
//Abhilash

Leave a Reply

Your email address will not be published. Required fields are marked *