Implementing custom iterators in JavaScript

JavaScript provides built-in iterators like for...of and Array.prototype.forEach() to iterate over data structures like arrays and objects. However, there may be cases where you need to create your own custom iterator to iterate over a custom data structure or to have better control over the iteration process.

In this blog post, we will explore how to implement custom iterators in JavaScript to iterate over your own data structures.

What is an Iterator?

An iterator is an object that provides a sequence of values or elements one at a time. It allows you to traverse through a collection or iterable object, providing you with the ability to access each item in the sequence or perform actions on them.

In JavaScript, an iterator is an object that must define a next() method. This method should return an object with two properties: value (the next value in the sequence) and done (a boolean indicating if there are any more values in the sequence).

Creating a Custom Iterator

To create a custom iterator in JavaScript, you need to define an object that implements the Iterable and Iterator protocols. The Iterable protocol requires the object to have a Symbol.iterator method that returns the iterator object itself. The Iterator protocol requires the iterator object to have a next() method.

Let’s take a look at an example of implementing a custom iterator for a custom data structure - a linked list.

class Node {
  constructor(value, next = null) {
    this.value = value;
    this.next = next;
  }
}

class LinkedList {
  constructor() {
    this.head = null;
  }

  [Symbol.iterator]() {
    let current = this.head;

    return {
      next: () => {
        if (current) {
          const value = current.value;
          current = current.next;
          return { value, done: false };
        }
        
        return { done: true };
      }
    };
  }

  add(value) {
    const newNode = new Node(value);
    
    if (!this.head) {
      this.head = newNode;
    } else {
      let current = this.head;
      while (current.next) {
        current = current.next;
      }
      current.next = newNode;
    }
  }
}

// Usage
const list = new LinkedList();
list.add(1);
list.add(2);
list.add(3);

for (const value of list) {
  console.log(value);
}

In this example, we have a LinkedList class that represents a linked list data structure. The LinkedList class implements the Symbol.iterator method, returning an iterator object. The iterator object’s next() method is implemented to traverse through the linked list and return the next value in the sequence.

The LinkedList class also has an add() method to add nodes to the linked list.

To iterate over the linked list, we can use the for...of loop, which automatically calls the iterator’s next() method on each iteration.

Conclusion

Implementing custom iterators in JavaScript allows you to have fine-grained control over the iteration process for your custom data structures. By implementing the Iterable and Iterator protocols, you can create iterators that can be used with the for...of loop or other iterator-consuming functions.

Custom iterators can be useful in scenarios where you need to iterate over complex data structures, perform operations on each element, or have more control over the iteration process.

#javascript #iterators