Easy patterns: Iterator

Ruslan Malogulko
ITNEXT
Published in
4 min readJan 21, 2019

--

This article is created in continuation of easy patterns series description and presents a behavioral pattern named an Iterator which provides a way to access the elements of some sequence (aggregate object) without exposing its internal representation.

Please refer to the other pattern articles as well:

Creational patterns:

Simple Factory

Factory method

Builder

Singleton

Abstract factory

Prototype

Structural patterns:

Adapter

Decorator

Bridge

Composite

Facade

Flyweight

Proxy

Behavioral patterns:

Visitor

Mediator

Observer

Memento

Iterator (this article)

Chain Of Responsibility

Strategy

State

The main essence

The key idea of Iterator pattern is to give you do an access and traversal out of the list in the manner you want. All this functionality will be placed in iterator object. The iterator class defines an interface for accessing the elements of the list. Also iterator object is responsible for keeping track of the current element and which elements have been traversed already.

For making an iterator instance client should supply the list to traverse for it. Once the iterator instance is ready, client can access the elements of the list sequentially.

Each Iterator class needs implementation of the common interface:

  • .currentItem method returns the current element in the list
  • .next method advances the current element to the next element in the aggregate object
  • .isDone method tests whether we reached end of the aggregate object.

The separation of the traversal mechanisms from the list object lets us define iterators for different traversal algorithms without declaring them in the list interface. For example some FilterIterator can return only elements from the list which satisfy some specific constraints.

This pattern includes two main roles:

  • Iterator — defines an interface and its implementation for accessing and traversing elements
  • Aggregate — defines an interface and its implementation for creating an Iterator object.

This pattern is also known as a Cursor and should be used for such cases:

  • to access an aggregate object content
  • to support multiple traversals of aggregate objects
  • to provide a uniform interface for traverse different aggregate structures

Example of use

In our example we will create an aggregated object and traverse over it with several iterator instances.

First iterator object will return relevant data items according to defined in aggregated object from and to boundaries in direct order.

Second iterator object will return relevant data in defined boundaries in reverse order.

It’s only a tiny demonstration of iteration control using Iterator pattern. Potentially, when you’re working with really complex data, this pattern can dramatically reduce complexity of your code and improve its readability because of concern separation between data and iteration logic.

To have a look on iteration process in custom way you can use for..of iteration instead of spread operator.

for (let item of ascendingIterator) {
console.log(item);
}

This would return the same result for you as the first example. Also feel free to play with it in Codepen:

Profit

The Iterator pattern makes possible to have a variations in the traversal of an aggregate. Complex aggregates may be traversed in many ways. Iterators make it easy to change the traversal algorithm — just replace the iterator instance with a different one.

Iterators simplify the interface of aggregate object because make possible to delegate traverse concerns completely to iterator object itself. Aggregate object should know and worry about how the iterator will go over its elements.

The aggregate object can have as many traversals in parallel as you want. An iterator keeps track on its own traversal state.

It’s possible not to have a length property for aggregate object and define if no elements left at runtime. Also it’s possible to iterate over not only arrays and lists.

Weak places

Usage of iterators can be an overhead in majority of cases. It needs implementation of specific methods to make it work. Its logic can be not obvious, especially if it’s used not in right places. Generally JavaScript uses Iterator pattern in many places: for String and Array objects, spread operator returns aggregate object for iterators.

It’s possible to create infinite iterators which generate infinite sequence of data. So, calling such iterator in tandem with for..of will be infinite as well. Such flexibility needs extra control on exit points in such code (putting some break operators for example).

Conclusion

Iterator is an object which can traverse over other objects. In JavaScript for such ability there is Symbol.iterator method should be implemented and return implementation of the next method.

There are many places in JavaScript which are using this pattern: spread operator, Array and String objects.

It’s often used with Composite pattern. Generally often Iterators applied to recursive structures.

Factory method often used with Iterator pattern to instantiate the appropriate Iterator subclass.

Memento pattern is often used in conjunction with the Iterator pattern. An iterator can use it to capture the state of an iteration internally.

If you found this article helpful, please hit the 👏 button and feel free to comment below!

--

--