Building Iterators

dnafication

Dina

Posted on April 4, 2022

Building Iterators

As programmers, one of the very first thing we learn is "the loop". There is always an array or list or collection which needs to be looped through, an object or map or dictionary whose keys and values require traversing. Iterations are key programming concept.

Arrays and Maps are collections of things and it should to be defined explicitly prior to iterating. You can start out with an empty array or a declaration and you can push items to it. Example:



const things = ['headphone', 'keyboard', 'mouse']
const friends = {
    tomo: 'nuxt expert',
    jt: 'writes nasty sqls',
    deco: 'the leader',
    van: 'php guru'
} 

things.push('cables')

// one of the ways of iterating arrays
for (const thing of things) {
  console.log(thing)
}

// iterate the key of objects
for (const name in friends) {
  console.log(`${name} - ${friends[name]}`)
}



Enter fullscreen mode Exit fullscreen mode

There are many ways of iterating over an array or an object. To name a few:

  • for(const i = 0; i < arr.length; i+=1)
  • for...of MDN
  • for...in MDN
  • while() MDN
  • Array.forEach MDN
  • Array.map MDN
  • Array.filter MDN
  • Array.reduce MDN

One thing about arrays or objects(Map, Set etc) is that you kind of know what you are getting. You could push things into an array but you know what you pushed. Its not dynamic. If something is in array, its there for good until you remove it. Also, its taking up that space in the memory.

Iterator protocol

What if you had a dynamic array that calculated what value you get in the next iteration? What if that worked based on a formula that you've built? You need to use iterator pattern. You'll notice that its very simple to implement. Its a protocol that is well known among JS programmers and also followed in other languages too. An iterator is an object that has next() method. Calling next() function on the object gives us the iterator result which is an object with two properties - done which is a boolean to hold the status of the iterator and value to hold whatever you want to return. Let's build a simple range iterator. This range iterator will allow us to create a range of numbers by providing a start, end and step.



// iterator protocol: an agreed interface
function numberRangeIterator(start, end, step) {
  let index = start
  return {
    next() {
      if (index > end) {
        return { done: true, value: 'thanks for using me' } // value is optional here but you can use it to return meta info
      }
      const value = index
      index += step
      return { done: false, value }
    }
  }
}

const iterator = numberRangeIterator(3, 30, 3)
let iteratorResult = iterator.next()
while (!iteratorResult.done) {
  console.log(iteratorResult.value)
  iteratorResult = iterator.next()
}


Enter fullscreen mode Exit fullscreen mode

You see? Its very simple and yet powerful. Two things to note:

  • the next function should return and object with done: true to indicate that there are no more elements. But it's not mandatory, you can have an iterator that runs forever!
  • you can have done: false or return just {value} and above code will just work fine.


function randomNumberIterator() {
  return {
    next() {
      return { done: false, value: Math.random() }
    }
  }
}

const rIterator = randomNumberIterator()
let rIteratorResult = rIterator.next()
while (!rIteratorResult.done) {
  console.log(rIteratorResult.value)
  rIteratorResult = rIterator.next()
}


Enter fullscreen mode Exit fullscreen mode

While I cannot think of when you'd use the iterator above, I just wanted to demonstrate an iterator that can generate random numbers infinitely.

Iterable protocol

Iterable protocol goes one step further by defining a standard within JS language for any object to return an iterator. An iterable is an object that implements an iterator method called [Symbol.iterator]. The best thing about using iterables over the iterator which we talked about above is that it allows us to use JS native apis for looping over the array such as for...of. Let's build our numberRangeIterator as an iterable.



class NumberRange {
  constructor(start, end, step) {
    this.start = start
    this.end = end
    this.step = step
  }
  // for an object/class to classify as iterable
  // it has to implement [Symbol.iterator]
  [Symbol.iterator]() {
    let index = this.start
    return {
      next: () => {
        if (index > this.end) {
          return { done: true }
        }
        const value = index
        index += this.step
        return { value }
      }
    }
  }
}

const myRange = new NumberRange(3, 30, 3)

for (const num of myRange) {
  console.log(num)
}


Enter fullscreen mode Exit fullscreen mode

It was almost the same amount of code to define the iterable class and we reused most of our code. However, the beauty is in the way we consume the iterator. Using for...of makes it look so clean and concise. I prefer this over the while loop above. But it doesn't stop here. There are other ways you can consume this iterable. You can use it with spread operator.



const myRange2 = new NumberRange(5, 20, 4)
console.log(...myRange2) // prints 5 9 13 17


Enter fullscreen mode Exit fullscreen mode

Or, destructure and assign it



const myRange2 = new NumberRange(5, 20, 4)

const [first, second, third] = myRange2
console.log(first, second, third) // prints 5 9 13


Enter fullscreen mode Exit fullscreen mode

There are other JS built-in APIs that accept iterables where you can pass your iterables such as Array.from(iterable), Set([iterable]), Promise.all(iterable) and even stream.Readable.from(iterable).

Read more about iterators here. You can pretty much treat it like your regular array but dynamic in nature and it will calculate your values only when it needs to. Things get a bit hairy though, when you start getting into the territory of async iterators but that is for another day.

💖 💪 🙅 🚩
dnafication
Dina

Posted on April 4, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related