Design Pattern: Iterator Pattern in TypeScript

Iterator pattern creates an interface for the client to access the internal list items of a collection. The implementation hides the underlying complexity from the client and makes it very simple for the client to traverse a list of items.

In this article, we are demonstrating Iterator pattern implementation in TypeScript. Check the implementation details and the examples.

Implementation

Follow the steps below to implement Iterator pattern in TypeScript-

  • Define an interface for the iterator. Declare methods for checking the availability of next item, getting the next item, and any other method as per requirement.
  • Define an iterator class and implement the iterator interface. Define property to store the item list and index of the currently accessing item.
  • Define a class for handling the iterator. and use it from the client.
  • Or the iterator can be generated and used by the client directly.

Here is a simple example of the implementation-

// Iterator pattern implementation in TypeScript

interface IteratorI<T> {
    hasNext(): boolean;
    next(): T;
}

class MyIterator<T> implements IteratorI<T> {
    private index: number = 0;
    private items: T[];

    constructor(items: T[]) {
        this.items = items;
    }
    
    hasNext(): boolean {
        return this.index < this.items.length;
    }

    next(): T {
        if(this.index >= this.items.length) {
            throw new Error('No more item')
        }

        const item = this.items[this.index];
        this.index++;

        return item;
    }
}

class Data<T> {
    private items: T[];

    constructor(items: T[]) {
        this.items = items;
    }

    getIterator(): IteratorI<T> {
        return new MyIterator(this.items);
    }
}

// Demo
const items: string[] = [
    'Item One',
    'Item Two',
    'Item Three',
    'Item Four',
    'Item Five',
];

const data = new Data<string>(items);
const iterator = data.getIterator();

while (iterator.hasNext()) {
    const currentItem = iterator.next();

    console.log(currentItem);
}

Output will be as below-

Item One
Item Two
Item Three
Item Four
Item Five

Examples

Here are a few examples of Iterator pattern implementation in TypeScript.

Example #1: Pagination Iterator

In this example, we are exploring the pagination of an app. Let’s check step-by-step, how can we use the iterator pattern to represent and traverse the pagination list.

Page Item Class [General]

  • Create file “page.ts”.
  • Create “Page” class. This represents a single item in the pagination.
  • Define methods for getting and/or setting page number and path.
// page.ts

class Page {
    private pageNumber: number = 0;
    private path: string | null = null;

    getNumber(): number {
        return this.pageNumber;
    }

    setNumber(pageNumber: number): void {
        this.pageNumber = pageNumber;
    }

    getPath(): string {
        if (this.path == null) {
            return "/page/" + this.pageNumber;
        }
        return this.path;
    }

    setPath(path: string): void {
        this.path = path;
    }
}

export default Page;

Page List Interface

  • Create file “abstract-page-list.ts”.
  • Create interface “AbstractPageList”.
  • Declare methods for handling items of the list, here we have “add”, “remove”, and “iterator” (this returns and iterator object).
// abstract_page_list.ts

import AbstractIterator from "./abstract-iterator";
import Page from "./page";

interface AbstractPageList {
    add(page: Page): void;
    remove(page: Page): void;
    iterator(): AbstractIterator;
}

export default AbstractPageList;

Page List Class

  • Create file “page-list.ts”.
  • Create class “PageList”.
  • Implement “AbstractPageList” interface for the class. Define methods – “add”, “remove”, “iterator”.
  • In the “iterator” method, create a new “Iterator” object and return that.
// page-list.ts

import AbstractIterator from "./abstract-iterator";
import AbstractPageList from "./abstract-page-list";
import Iterator from "./iterator";
import Page from "./page";


class PageList implements AbstractPageList {
    private pages = new Map<number, Page>();;

    add(page: Page): void {
        this.pages.set(page.getNumber(), page);
    }

    remove(page: Page): void {
        this.pages.delete(page.getNumber());
    }

    iterator(): AbstractIterator {
        return new Iterator(this.pages);
    }
}

export default PageList;

Iterator Interface

  • Create file “abstract-iterator.ts”.
  • Create interface “AbstractIterator”.
  • Declare methods – “hasNext” (for checking if all the items are traversed or still some items left), “next” (to get the next item).
// abstract-iterator.ts

import Page from "./page";

interface AbstractIterator {
    hasNext(): boolean;
    next(): Page | undefined;
}

export default AbstractIterator;

Iterator Class

  • Create file “iterator.ts”.
  • Create class “Iterator”.
  • Implement interface “AbstractIterator” for the class. Define methods “hasNext” and “next” for the class.
  • Define properties – “currentPosition” (for storing the position of currently accessing item), and “pages” (for storing the list of pages).
// iterator.ts

import AbstractIterator from "./abstract-iterator";
import Page from "./page";

class Iterator implements AbstractIterator {
    private currentPosition: number = 0;
    private readonly pages = new Map<number, Page>();

    constructor(pages: Map<number, Page>) {
        this.pages = pages;
    }

    hasNext(): boolean {
        return this.currentPosition < this.pages.size;
    }

    next(): Page | undefined {
        const page = this.pages.get(this.currentPosition);
        this.currentPosition++;

        return page;
    }
}

export default Iterator;

Demo

For using the implementation we have created a list of items. Then accessed the iterator from that.

Then in a loop, we are checking if any other item (next item) is available or not. If available then we are accessing that page.

For generating the list of items/pages, we have a dummy function here, named “populatePageList”.

// demo.ts

import Page from "./page";
import PageList from "./page-list";

// Dummy function to populate the pagination list
function populatePageList(): PageList {
    let pageList = new PageList();

    for (let i = 0; i < 10; i++) {
        let page = new Page();
        page.setNumber(i);

        pageList.add(page);
    }

    return pageList;
}


const pageList = populatePageList();
const paginator = pageList.iterator();

while (paginator.hasNext()) {
    const page = paginator.next();

    if (page !== undefined) {
        console.log("Page Number: " + page.getNumber());
        console.log("Page Path: " + page.getPath());
    }
}

Output

following output will be generated-

Page Number: 0
Page Path: /page/0

Page Number: 1
Page Path: /page/1

Page Number: 2
Page Path: /page/2

Page Number: 3
Page Path: /page/3

Page Number: 4
Page Path: /page/4

Page Number: 5
Page Path: /page/5

Page Number: 6
Page Path: /page/6

Page Number: 7
Page Path: /page/7

Page Number: 8
Page Path: /page/8

Page Number: 9
Page Path: /page/9

Source Code

Use the following link to get the source code:

Other Code Implementations

Use the following links to check the Iterator pattern implementation in other programming languages.

Leave a Comment


The reCAPTCHA verification period has expired. Please reload the page.