# Iterators - Modern Javascript

ES6 has introduced new ways of working with functions and objects in Javascript. In this article, we are going to discuss **Iterators** and **Generators**.

## What are Iterables??
Before discussing Iterators, we need an understanding of Iterables in Javascript. As the name suggests, Iterables are the objects that allow us to iterate over them. Most likely you must have used Iterables before like Array, String, etc. In terms of data structure, Objects that have the `Symbol.iterator()` method are Iterables. We will discuss more about `Symbol.iterator()` in next section .

A ``for...of` can be used to iterate a collection. The `for...of` loop requires an iterable. Otherwise, it will throw `TypeError`.

For example,
```
let collectn = [1, 2, 3];
for(let n of collectn){
    console.log(n);  
}
```

Following objects are already Iterables in Javascript

- Arrays
- Strings - can iterate over each character
- Maps - can iterate using Key-value pair
- Sets - can iterate over values


## <a id="Iterators"></a>Iterators
Iterators are a new way to iterate a sequence of values or any collection. They are objects which allow us to access one value at a time from collections. In terms of structure, Iterators are the object which implements below interface
```
interface Iterator<T> {
    next(value?: any): IteratorResult<T>;
    return?(value?: any): IteratorResult<T>;
    throw?(e?: any): IteratorResult<T>;
}
```
`next()` allows us to access the next element of the iterable (one at a time). IteratorResult is the combination of 'value' and 'done' properties.
```
interface IteratorResult<T> {
    value: T;
    done: boolean;    
}
```

- `value` property can be of any data type and returns the current value in the sequence.
- `done` property is a boolean value that indicates whether the iteration is complete or not. It returns true once the iteration is complete, else false.

Let's check the above code block with Iterators
```
let collectn: number[] = [1, 2, 3];
let nIterator: any = collectn[Symbol.iterator]();
console.log(nIterator.next()); // value -> 1, done -> false
console.log(nIterator.next()); // value -> 2, done -> false
console.log(nIterator.next()); // value -> 3, done -> false
console.log(nIterator.next()); // value -> undefined, done -> true
```
### Usage
Some of you must be thinking it is easy to use methods like for, foreach, for..of, while, etc to iterate over objects. 
Consider a complex object `myBooks` having nested objects

```
const myBooks = {
  English: {
    'Dr.Mark',
    'J. K. Rowling'
  },
  Science: {
    'Dr.Neal',
    'Rober Aslom'
  },
  Fiction: {
    'Terry Machinst',
    'J. K. Rowling'
  },
}
```
Can you think about how we can iterate over this object? If we try to use the `for...of` loop here, we will get the following error:
```
for(let auth of myBooks){
//Remember `for...of` loop requires an `Iterable` to iterate. 
  console.log(auth);              //TypeError: { } is not iterable
}
```
In order to fetch all the writers, we need to use multiple loops. There is no direct way to get all the writers from our custom object `myBooks`.  we need a method in our object that can return our data sequentially.
In order to iterate, we need to make our custom object `Iterable`

Let's add a method in our object that returns all writers.
```
const myBooks = {
  writers: {
    English: {
      'Dr.Mark',
      'J. K. Rowling'
    },
    Science: {
      'Dr.Neal',
      'Rober Aslom'
    },
    Fiction: {
      'Terry Machinst',
      'J. K. Rowling'
    },
  },
  getWriters(){
    const writers = [];
    
    for(const writer of this.writers.English){
      console.log(writer);
    }
    for(const writer of this.writers.Science){
      console.log(writer);
    }
    for(const writer of this.writers.Fiction){
      console.log(writer);
    }
  }
}
```
The above code will solve our problem and returns writers' data sequentially. But this code block still has some limitations. 
For this implementation, the developer has to know the exact data structure of our custom objects like property name and return type of the method that returns all the data. Different developers will deal with it differently. 
Here `Iterators` come into play. Let's solve the above problem using `Iterators `.
```
const myBooks = {
  English: {
    'Dr.Mark',
    'J. K. Rowling'
  },
  Science: {
    'Dr.Neal',
    'Rober Aslom'
  },
  Fiction: {
    'Terry Machinst',
    'J. K. Rowling'
  },
},
  //Remember: Iterators are the objects which implement iterator interface
  [Symbol.iterator]() { 
  
    const subjects= Object.values(this.myBooks);
    
    let currentWriterIndex = 0;
    let currentSubjectIndex = 0;

    return {
      //Iterators have the next() which will return an object with keys value and done.
      next() {  
        const writers = subjects[currentSubjectIndex];
      
        const isLastWriter = !(currentWriterIndex < writers.length);
        if (isLastWriter) {
          currentSubjectIndex++;
          currentWriterIndex = 0;
        }
        
        const isLastSubject= !(currentSubjectIndex < subjects.length);
        if (isLastSubject) {
          return {
            value: undefined,
            done: true            <- done indicates whether iteration is complete or not
          };
        }
        
        return {
          value: subjects[currentSubjectIndex][currentWriterIndex++],
          done: false
        }
      }
    };
  }
}
```
We have created an `Iterator` in our myBooks custom object using `System.iterator`.  As mentioned before, Iterator has the `next()` method which will return an object with keys `value` and `done`. `value` property will contain the current value, and `done` property indicates whether iteration is complete or not. 

Now with `iterator` implemented in our custom object, we can now use our custom object simply using `for..of` or any other iteration method.
```
for(let auth of myBooks)
{
    console.log(auth);
}
```
Custom Iterators are a useful tool. Their implementation requires careful programming as it can also lead to an infinite loop. 

In our  [next ](https://devnuisance.hashnode.dev/generators) article, we are going to discuss **Generators**. They provide a powerful alternative to **Iterators** by defining a single function whose execution is not continuous.

I hope this article will help you to understand the `Iterables` and `Iterators`. If you have any suggestions, just let me know in the comments section.
