Iterator Design Pattern in JavaScript

1. Definition

The Iterator Design Pattern provides a way to access the elements of a collection object in a sequential manner without any need to know its underlying representation. It decouples algorithms from containers.

2. Problem Statement

Imagine you have a collection, such as an array or a list. You want to provide a way to access its elements without exposing its underlying structure. Also, you might want to traverse the collection in different ways depending on the situation.

3. Solution

Use an iterator to traverse the collection. The iterator encapsulates the details of accessing and traversing the items. If the collection needs to be traversed in a different way, you can use a different iterator.

4. Real-World Use Cases

1. Traversing through different types of collections (arrays, trees, graphs).

2. Accessing a database and iterating over a recordset.

3. Pagination in web applications.

5. Implementation Steps

1. Define an Iterator interface with methods: next(), hasNext().

2. Implement the Iterator interface for the specific collection.

3. Optionally, implement an Iterable interface for the collection to return its iterator.

6. Implementation in JavaScript

// Iterator Interface
class Iterator {
    hasNext() {}
    next() {}
}
// Concrete Iterator
class ArrayIterator extends Iterator {
    constructor(items) {
        super();
        this.index = 0;
        this.items = items;
    }
    hasNext() {
        return this.index < this.items.length;
    }
    next() {
        return this.items[this.index++];
    }
}
// Iterable Interface
class Iterable {
    createIterator() {}
}
// Concrete Iterable
class ArrayList extends Iterable {
    constructor() {
        super();
        this.items = [];
    }
    createIterator() {
        return new ArrayIterator(this.items);
    }
    add(item) {
        this.items.push(item);
    }
}
// Client Code
const list = new ArrayList();
list.add(1);
list.add(2);
list.add(3);
const iterator = list.createIterator();
while (iterator.hasNext()) {
    console.log(iterator.next());
}

Output:

1
2
3

Explanation:

1. We defined an Iterator interface and a concrete ArrayIterator that can iterate over arrays.

2. We then defined an Iterable interface and a concrete ArrayList which represents a list that can produce an iterator.

3. In the client code, we added three items to the list and then used its iterator to access and print each item.

4. The iterator encapsulates the logic of traversal, and the client code doesn't need to know the underlying structure of the ArrayList.

7. When to use?

The Iterator Pattern is useful when:

1. You want to access the contents of a collection without exposing its internal structure.

2. You need several different ways of traversing a collection.

3. You want to provide a uniform interface for iterating over different aggregate structures (e.g., different types of collections).


Comments