# What are Iterables in JavaScript?

Iterables give the ability for a JS object to be iterated and to be used in a 'for of' loop.  

A typical use case for a 'for of' loop is when looping across an array. 

 ```javascript 
const arr = [1,2,3,4,5]
for(let num of arr){
 console.log(num); 
}
```

However, we can't loop a standard JavaScript object, and if you try to do it, you will get a syntax error. 

Therefore we extend a JS Object with the iterable capability to loop over its contents in whichever way we want. 

### Let's convert a simple object into an iterator

For example, take a look at this range object with two attributes: a start value and an end value, defining a range.

The iterator would start looping from the starting value until it reaches the ending value.

```javascript 
 const range = {
   start:1,
   end: 6 
 }
```
To convert this range object to an iterable, we need to define a **unique attribute** inside the object, known as the **'Symbol.iterator'**.

The **'Symbol.iterator' attribute is a function** that should return an object which has a function called 'next'. 

This **next function** is what makes iterating possible. 

The 'next' function should return an object with **2** attributes: **' value'** and **'done'**.

 **'value' attribute** can be any value that is being iterated (e.g., string, object, etc.) 

**'done' attribute** should be a boolean, which, when true, means that the iteration is over
The ‘range’ object implements the ‘Symbol.iterator’ attribute and turns into an iterable.

The current and last variables are used for checking the looping criteria inside the next function. The current variable is the return value of the iterator. 

```javascript 
const range = {
  start: 1, 
  end: 6
}

range[Symbol.iterator] = function () {
 return {
   current: this.start, 
   last: this.end,

   next: function () {
      if (this.current <= this.last) {
        return {value: this.current++, done: false} 
      } else {
        return {done: true}
      }
   }
   
 }
}

for (let num of range){
  console.log(num); 
}
```

Like how an array is iterated using a 'for of' loop, we can iterate the 'range' object. We can also use the spread operator on the 'range' object, just as we do in an array.

```javascript 
const arr = [1,2,3,4,5,6] 

for (let num of arr) {
 console.log(num); 
}

console.log([...arr]); //prints [1,2,3,4,5,6]

//Here is our 'range' object, it starts from 1 to 6 
for (let num of range) {
  console.log(num); 
}

console.log([...range]); //prints [1,2,3,4,5,6]
```

We can also implement the **‘Symbol.iterator’ attribute inside the ‘range’ object** as well, as opposed to doing it outside the object.


```javascript 
const range = {
  start: 1,
  end: 6,
  
  [Symbol.iterator]: function () {
    return {
      current: this.start,
      last: this.end,
      
      next: function () {
        if (this.current <= this.last) {
          return {
            value: this.current++,
            done: false,
          }
        } else {
          return { done: true, value:this.current }
        }
      },
      
    }
  },
  
}
console.log([...range]) //prints [1,2,3,4,5,6] 
```

Here is an equivalent alternative of the above code snippet, however this implementation of the iterator is more ‘cleaner’ compared to the above code snippet 

```javascript 
const range = {
  start: 1,
  end: 6,

  [Symbol.iterator]() {
    this.current = this.start
    return this
  },

  next() {
    if (this.current <= this.end) {
      return { done: false, value: this.current++ }
    } else {
      return { done: true }
    }
  },
  
}

console.log([...range]) //prints [1,2,3,4,5,6] 
```

### Resources

All the code implementations can be found in the JS Fiddle link below
https://jsfiddle.net/xwmot3kp/

### References 

1.  [MDN Web Doc on JS Iterables](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Iterators_and_Generators#iterables) 

2.  [MDN Reference on Symbol.iterable](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/iterator) 

