Linked List (Doubly)

The @abp/utils package provides a useful data structure known as a doubly linked list. It is availabe in both Angular (via an import) and MVC (via abp.utils.common global object).

Briefly, a doubly linked list is a series of records (a.k.a. nodes) which has information on the previous node, the next node, and its own value (or data).

Getting Started

To create a doubly linked list, all you have to do is to create a new instance of it:

In Angular:

  1. import { LinkedList } from '@abp/utils';
  2. const list = new LinkedList();

In MVC:

  1. var list = new abp.utils.common.LinkedList();

The constructor does not get any parameters.

Usage

How to Add New Nodes

There are several methods to create new nodes in a linked list and all of them are separately available as well as revealed by add and addMany methods.

addHead(value)

  1. addHead(value: T): ListNode<T>

Adds a node with given value as the first node in list:

  1. list.addHead('a');
  2. // "a"
  3. list.addHead('b');
  4. // "b" <-> "a"
  5. list.addHead('c');
  6. // "c" <-> "b" <-> "a"

addManyHead(values)

  1. addManyHead(values: T[]): ListNode<T>[]

Adds multiple nodes with given values as the first nodes in list:

  1. list.addManyHead(['a', 'b', 'c']);
  2. // "a" <-> "b" <-> "c"
  3. list.addManyHead(['x', 'y', 'z']);
  4. // "x" <-> "y" <-> "z" <-> "a" <-> "b" <-> "c"

addTail(value)

  1. addTail(value: T): ListNode<T>

Adds a node with given value as the last node in list:

  1. list.addTail('a');
  2. // "a"
  3. list.addTail('b');
  4. // "a" <-> "b"
  5. list.addTail('c');
  6. // "a" <-> "b" <-> "c"

addManyTail(values)

  1. addManyTail(values: T[]): ListNode<T>[]

Adds multiple nodes with given values as the last nodes in list:

  1. list.addManyTail(['a', 'b', 'c']);
  2. // "a" <-> "b" <-> "c"
  3. list.addManyTail(['x', 'y', 'z']);
  4. // "a" <-> "b" <-> "c" <-> "x" <-> "y" <-> "z"

addAfter(value, previousValue [, compareFn])

  1. addAfter(value: T, previousValue: T, compareFn?: ListComparisonFn<T>): ListNode<T>

Adds a node with given value after the first node that has the previous value:

  1. list.addTail('a');
  2. list.addTail('b');
  3. list.addTail('b');
  4. list.addTail('c');
  5. // "a" <-> "b" <-> "b" <-> "c"
  6. list.addAfter('x', 'b');
  7. // "a" <-> "b" <-> "x" <-> "b" <-> "c"

You may pass a custom compare function to detect the searched value:

  1. list.addTail({ x: 1 });
  2. list.addTail({ x: 2 });
  3. list.addTail({ x: 3 });
  4. // {"x":1} <-> {"x":2} <-> {"x":3}
  5. list.addAfter(
  6. { x: 0 },
  7. 2,
  8. (value, searchedValue) => value.x === searchedValue
  9. );
  10. // {"x":1} <-> {"x":2} <-> {"x":0} <-> {"x":3}

The default compare function checks deep equality, so you will rarely need to pass that parameter.

addManyAfter(values, previousValue [, compareFn])

  1. addManyAfter(values: T[], previousValue: T, compareFn?: ListComparisonFn<T>): ListNode<T>[]

Adds multiple nodes with given values after the first node that has the previous value:

  1. list.addManyTail(['a', 'b', 'b', 'c']);
  2. // "a" <-> "b" <-> "b" <-> "c"
  3. list.addManyAfter(['x', 'y'], 'b');
  4. // "a" <-> "b" <-> "x" <-> "y" <-> "b" <-> "c"

You may pass a custom compare function to detect the searched value:

  1. list.addManyTail([{ x: 1 },{ x: 2 },{ x: 3 }]);
  2. // {"x":1} <-> {"x":2} <-> {"x":3}
  3. list.addManyAfter(
  4. [{ x: 4 }, { x: 5 }],
  5. 2,
  6. (value, searchedValue) => value.x === searchedValue
  7. );
  8. // {"x":1} <-> {"x":2} <-> {"x":4} <-> {"x":5} <-> {"x":3}

The default compare function checks deep equality, so you will rarely need to pass that parameter.

addBefore(value, nextValue [, compareFn])

  1. addBefore(value: T, nextValue: T, compareFn?: ListComparisonFn<T>): ListNode<T>

Adds a node with given value before the first node that has the next value:

  1. list.addTail('a');
  2. list.addTail('b');
  3. list.addTail('b');
  4. list.addTail('c');
  5. // "a" <-> "b" <-> "b" <-> "c"
  6. list.addBefore('x', 'b');
  7. // "a" <-> "x" <-> "b" <-> "b" <-> "c"

You may pass a custom compare function to detect the searched value:

  1. list.addTail({ x: 1 });
  2. list.addTail({ x: 2 });
  3. list.addTail({ x: 3 });
  4. // {"x":1} <-> {"x":2} <-> {"x":3}
  5. list.addBefore(
  6. { x: 0 },
  7. 2,
  8. (value, searchedValue) => value.x === searchedValue
  9. );
  10. // {"x":1} <-> {"x":0} <-> {"x":2} <-> {"x":3}

The default compare function checks deep equality, so you will rarely need to pass that parameter.

addManyBefore(values, nextValue [, compareFn])

  1. addManyBefore(values: T[], nextValue: T, compareFn?: ListComparisonFn<T>): ListNode<T>[]

Adds multiple nodes with given values before the first node that has the next value:

  1. list.addManyTail(['a', 'b', 'b', 'c']);
  2. // "a" <-> "b" <-> "b" <-> "c"
  3. list.addManyBefore(['x', 'y'], 'b');
  4. // "a" <-> "x" <-> "y" <-> "b" <-> "b" <-> "c"

You may pass a custom compare function to detect the searched value:

  1. list.addManyTail([{ x: 1 },{ x: 2 },{ x: 3 }]);
  2. // {"x":1} <-> {"x":2} <-> {"x":3}
  3. list.addManyBefore(
  4. [{ x: 4 }, { x: 5 }],
  5. 2,
  6. (value, searchedValue) => value.x === searchedValue
  7. );
  8. // {"x":1} <-> {"x":4} <-> {"x":5} <-> {"x":2} <-> {"x":3}

The default compare function checks deep equality, so you will rarely need to pass that parameter.

addByIndex(value, position)

  1. addByIndex(value: T, position: number): ListNode<T>

Adds a node with given value at the specified position in the list:

  1. list.addTail('a');
  2. list.addTail('b');
  3. list.addTail('c');
  4. // "a" <-> "b" <-> "c"
  5. list.addByIndex('x', 2);
  6. // "a" <-> "b" <-> "x" <-> "c"

It works with negative index too:

  1. list.addTail('a');
  2. list.addTail('b');
  3. list.addTail('c');
  4. // "a" <-> "b" <-> "c"
  5. list.addByIndex('x', -1);
  6. // "a" <-> "b" <-> "x" <-> "c"

addManyByIndex(values, position)

  1. addManyByIndex(values: T[], position: number): ListNode<T>[]

Adds multiple nodes with given values at the specified position in the list:

  1. list.addManyTail(['a', 'b', 'c']);
  2. // "a" <-> "b" <-> "c"
  3. list.addManyByIndex(['x', 'y'], 2);
  4. // "a" <-> "b" <-> "x" <-> "y" <-> "c"

It works with negative index too:

  1. list.addManyTail(['a', 'b', 'c']);
  2. // "a" <-> "b" <-> "c"
  3. list.addManyByIndex(['x', 'y'], -1);
  4. // "a" <-> "b" <-> "x" <-> "y" <-> "c"

add(value).head()

  1. add(value: T).head(): ListNode<T>

Adds a node with given value as the first node in list:

  1. list.add('a').head();
  2. // "a"
  3. list.add('b').head();
  4. // "b" <-> "a"
  5. list.add('c').head();
  6. // "c" <-> "b" <-> "a"

This is an alternative API for addHead.

add(value).tail()

  1. add(value: T).tail(): ListNode<T>

Adds a node with given value as the last node in list:

  1. list.add('a').tail();
  2. // "a"
  3. list.add('b').tail();
  4. // "a" <-> "b"
  5. list.add('c').tail();
  6. // "a" <-> "b" <-> "c"

This is an alternative API for addTail.

add(value).after(previousValue [, compareFn])

  1. add(value: T).after(previousValue: T, compareFn?: ListComparisonFn<T>): ListNode<T>

Adds a node with given value after the first node that has the previous value:

  1. list.add('a').tail();
  2. list.add('b').tail();
  3. list.add('b').tail();
  4. list.add('c').tail();
  5. // "a" <-> "b" <-> "b" <-> "c"
  6. list.add('x').after('b');
  7. // "a" <-> "b" <-> "x" <-> "b" <-> "c"

You may pass a custom compare function to detect the searched value:

  1. list.add({ x: 1 }).tail();
  2. list.add({ x: 2 }).tail();
  3. list.add({ x: 3 }).tail();
  4. // {"x":1} <-> {"x":2} <-> {"x":3}
  5. list
  6. .add({ x: 0 })
  7. .after(2, (value, searchedValue) => value.x === searchedValue);
  8. // {"x":1} <-> {"x":2} <-> {"x":0} <-> {"x":3}

This is an alternative API for addAfter.

The default compare function checks deep equality, so you will rarely need to pass that parameter.

add(value).before(nextValue [, compareFn])

  1. add(value: T).before(nextValue: T, compareFn?: ListComparisonFn<T>): ListNode<T>

Adds a node with given value before the first node that has the next value:

  1. list.add('a').tail();
  2. list.add('b').tail();
  3. list.add('b').tail();
  4. list.add('c').tail();
  5. // "a" <-> "b" <-> "b" <-> "c"
  6. list.add('x').before('b');
  7. // "a" <-> "x" <-> "b" <-> "b" <-> "c"

You may pass a custom compare function to detect the searched value:

  1. list.add({ x: 1 }).tail();
  2. list.add({ x: 2 }).tail();
  3. list.add({ x: 3 }).tail();
  4. // {"x":1} <-> {"x":2} <-> {"x":3}
  5. list
  6. .add({ x: 0 })
  7. .before(2, (value, searchedValue) => value.x === searchedValue);
  8. // {"x":1} <-> {"x":0} <-> {"x":2} <-> {"x":3}

This is an alternative API for addBefore.

The default compare function checks deep equality, so you will rarely need to pass that parameter.

add(value).byIndex(position)

  1. add(value: T).byIndex(position: number): ListNode<T>

Adds a node with given value at the specified position in the list:

  1. list.add('a').tail();
  2. list.add('b').tail();
  3. list.add('c').tail();
  4. // "a" <-> "b" <-> "c"
  5. list.add('x').byIndex(2);
  6. // "a" <-> "b" <-> "x" <-> "c"

It works with negative index too:

  1. list.add('a').tail();
  2. list.add('b').tail();
  3. list.add('c').tail();
  4. // "a" <-> "b" <-> "c"
  5. list.add('x').byIndex(-1);
  6. // "a" <-> "b" <-> "x" <-> "c"

This is an alternative API for addByIndex.

addMany(values).head()

  1. addMany(values: T[]).head(): ListNode<T>[]

Adds multiple nodes with given values as the first nodes in list:

  1. list.addMany(['a', 'b', 'c']).head();
  2. // "a" <-> "b" <-> "c"
  3. list.addMany(['x', 'y', 'z']).head();
  4. // "x" <-> "y" <-> "z" <-> "a" <-> "b" <-> "c"

This is an alternative API for addManyHead.

addMany(values).tail()

  1. addMany(values: T[]).tail(): ListNode<T>[]

Adds multiple nodes with given values as the last nodes in list:

  1. list.addMany(['a', 'b', 'c']).tail();
  2. // "a" <-> "b" <-> "c"
  3. list.addMany(['x', 'y', 'z']).tail();
  4. // "a" <-> "b" <-> "c" <-> "x" <-> "y" <-> "z"

This is an alternative API for addManyTail.

addMany(values).after(previousValue [, compareFn])

  1. addMany(values: T[]).after(previousValue: T, compareFn?: ListComparisonFn<T>): ListNode<T>[]

Adds multiple nodes with given values after the first node that has the previous value:

  1. list.addMany(['a', 'b', 'b', 'c']).tail();
  2. // "a" <-> "b" <-> "b" <-> "c"
  3. list.addMany(['x', 'y']).after('b');
  4. // "a" <-> "b" <-> "x" <-> "y" <-> "b" <-> "c"

You may pass a custom compare function to detect the searched value:

  1. list.addMany([{ x: 1 }, { x: 2 }, { x: 3 }]).tail();
  2. // {"x":1} <-> {"x":2} <-> {"x":3}
  3. list
  4. .addMany([{ x: 4 }, { x: 5 }])
  5. .after(2, (value, searchedValue) => value.x === searchedValue);
  6. // {"x":1} <-> {"x":2} <-> {"x":4} <-> {"x":5} <-> {"x":3}

This is an alternative API for addManyAfter.

The default compare function checks deep equality, so you will rarely need to pass that parameter.

addMany(values).before(nextValue [, compareFn])

  1. addMany(values: T[]).before(nextValue: T, compareFn?: ListComparisonFn<T>): ListNode<T>[]

Adds multiple nodes with given values before the first node that has the next value:

  1. list.addMany(['a', 'b', 'b', 'c']).tail();
  2. // "a" <-> "b" <-> "b" <-> "c"
  3. list.addMany(['x', 'y']).before('b');
  4. // "a" <-> "x" <-> "y" <-> "b" <-> "b" <-> "c"

You may pass a custom compare function to detect the searched value:

  1. list.addMany([{ x: 1 }, { x: 2 }, { x: 3 }]).tail();
  2. // {"x":1} <-> {"x":2} <-> {"x":3}
  3. list
  4. .addMany([{ x: 4 }, { x: 5 }])
  5. .before(2, (value, searchedValue) => value.x === searchedValue);
  6. // {"x":1} <-> {"x":4} <-> {"x":5} <-> {"x":2} <-> {"x":3}

This is an alternative API for addManyBefore.

The default compare function checks deep equality, so you will rarely need to pass that parameter.

addMany(values).byIndex(position)

  1. addMany(values: T[]).byIndex(position: number): ListNode<T>[]

Adds multiple nodes with given values at the specified position in the list:

  1. list.addMany(['a', 'b', 'c']).tail();
  2. // "a" <-> "b" <-> "c"
  3. list.addMany(['x', 'y']).byIndex(2);
  4. // "a" <-> "b" <-> "x" <-> "y" <-> "c"

It works with negative index too:

  1. list.addMany(['a', 'b', 'c']).tail();
  2. // "a" <-> "b" <-> "c"
  3. list.addMany(['x', 'y']).byIndex(-1);
  4. // "a" <-> "b" <-> "x" <-> "y" <-> "c"

This is an alternative API for addManyByIndex.

How to Remove Nodes

There are a few methods to remove nodes from a linked list and all of them are separately available as well as revealed from a drop method.

dropHead()

  1. dropHead(): ListNode<T> | undefined

Removes the first node from the list:

  1. list.addMany(['a', 'b', 'c']).tail();
  2. // "a" <-> "b" <-> "c"
  3. list.dropHead();
  4. // "b" <-> "c"

dropManyHead(count)

  1. dropManyHead(count: number): ListNode<T>[]

Removes the first nodes from the list based on given count:

  1. list.addMany(['a', 'b', 'c']).tail();
  2. // "a" <-> "b" <-> "c"
  3. list.dropManyHead(2);
  4. // "c"

dropTail()

  1. dropTail(): ListNode<T> | undefined

Removes the last node from the list:

  1. list.addMany(['a', 'b', 'c']).tail();
  2. // "a" <-> "b" <-> "c"
  3. list.dropTail();
  4. // "a" <-> "b"

dropManyTail(count)

  1. dropManyTail(count: number): ListNode<T>[]

Removes the last nodes from the list based on given count:

  1. list.addMany(['a', 'b', 'c']).tail();
  2. // "a" <-> "b" <-> "c"
  3. list.dropManyTail(2);
  4. // "a"

dropByIndex(position)

  1. dropByIndex(position: number): ListNode<T> | undefined

Removes the node with the specified position from the list:

  1. list.addMany(['a', 'b', 'c']).tail();
  2. // "a" <-> "b" <-> "c"
  3. list.dropByIndex(1);
  4. // "a" <-> "c"

It works with negative index too:

  1. list.addMany(['a', 'b', 'c']).tail();
  2. // "a" <-> "b" <-> "c"
  3. list.dropByIndex(-2);
  4. // "a" <-> "c"

dropManyByIndex(count, position)

  1. dropManyByIndex(count: number, position: number): ListNode<T>[]

Removes the nodes starting from the specified position from the list based on given count:

  1. list.addMany(['a', 'b', 'c', 'd']).tail();
  2. // "a" <-> "b" <-> "c" <-> "d
  3. list.dropManyByIndex(2, 1);
  4. // "a" <-> "d"

It works with negative index too:

  1. list.addMany(['a', 'b', 'c', 'd']).tail();
  2. // "a" <-> "b" <-> "c" <-> "d
  3. list.dropManyByIndex(2, -2);
  4. // "a" <-> "d"

dropByValue(value [, compareFn])

  1. dropByValue(value: T, compareFn?: ListComparisonFn<T>): ListNode<T> | undefined

Removes the first node with given value from the list:

  1. list.addMany(['a', 'x', 'b', 'x', 'c']).tail();
  2. // "a" <-> "x" <-> "b" <-> "x" <-> "c"
  3. list.dropByValue('x');
  4. // "a" <-> "b" <-> "x" <-> "c"

You may pass a custom compare function to detect the searched value:

  1. list.addMany([{ x: 1 }, { x: 0 }, { x: 2 }, { x: 0 }, { x: 3 }]).tail();
  2. // {"x":1} <-> {"x":0} <-> {"x":2} <-> {"x":0} <-> {"x":3}
  3. list.dropByValue(0, (value, searchedValue) => value.x === searchedValue);
  4. // {"x":1} <-> {"x":2} <-> {"x":0} <-> {"x":3}

The default compare function checks deep equality, so you will rarely need to pass that parameter.

dropByValueAll(value [, compareFn])

  1. dropByValueAll(value: T, compareFn?: ListComparisonFn<T>): ListNode<T>[]

Removes all nodes with given value from the list:

  1. list.addMany(['a', 'x', 'b', 'x', 'c']).tail();
  2. // "a" <-> "x" <-> "b" <-> "x" <-> "c"
  3. list.dropByValueAll('x');
  4. // "a" <-> "b" <-> "c"

You may pass a custom compare function to detect the searched value:

  1. list.addMany([{ x: 1 }, { x: 0 }, { x: 2 }, { x: 0 }, { x: 3 }]).tail();
  2. // {"x":1} <-> {"x":0} <-> {"x":2} <-> {"x":0} <-> {"x":3}
  3. list.dropByValue(0, (value, searchedValue) => value.x === searchedValue);
  4. // {"x":1} <-> {"x":2} <-> {"x":3}

The default compare function checks deep equality, so you will rarely need to pass that parameter.

drop().head()

  1. drop().head(): ListNode<T> | undefined

Removes the first node in list:

  1. list.addMany(['a', 'b', 'c']).tail();
  2. // "a" <-> "b" <-> "c"
  3. list.drop().head();
  4. // "b" <-> "c"

This is an alternative API for dropHead.

drop().tail()

  1. drop().tail(): ListNode<T> | undefined

Removes the last node in list:

  1. list.addMany(['a', 'b', 'c']).tail();
  2. // "a" <-> "b" <-> "c"
  3. list.drop().tail();
  4. // "a" <-> "b"

This is an alternative API for dropTail.

drop().byIndex(position)

  1. drop().byIndex(position: number): ListNode<T> | undefined

Removes the node with the specified position from the list:

  1. list.addMany(['a', 'b', 'c']).tail();
  2. // "a" <-> "b" <-> "c"
  3. list.drop().byIndex(1);
  4. // "a" <-> "c"

It works with negative index too:

  1. list.addMany(['a', 'b', 'c']).tail();
  2. // "a" <-> "b" <-> "c"
  3. list.drop().byIndex(-2);
  4. // "a" <-> "c"

This is an alternative API for dropByIndex.

drop().byValue(value [, compareFn])

  1. drop().byValue(value: T, compareFn?: ListComparisonFn<T>): ListNode<T> | undefined

Removes the first node with given value from the list:

  1. list.addMany(['a', 'x', 'b', 'x', 'c']).tail();
  2. // "a" <-> "x" <-> "b" <-> "x" <-> "c"
  3. list.drop().byValue('x');
  4. // "a" <-> "b" <-> "x" <-> "c"

You may pass a custom compare function to detect the searched value:

  1. list.addMany([{ x: 1 }, { x: 0 }, { x: 2 }, { x: 0 }, { x: 3 }]).tail();
  2. // {"x":1} <-> {"x":0} <-> {"x":2} <-> {"x":0} <-> {"x":3}
  3. list
  4. .drop()
  5. .byValue(0, (value, searchedValue) => value.x === searchedValue);
  6. // {"x":1} <-> {"x":2} <-> {"x":0} <-> {"x":3}

This is an alternative API for dropByValue.

The default compare function checks deep equality, so you will rarely need to pass that parameter.

drop().byValueAll(value [, compareFn])

  1. drop().byValueAll(value: T, compareFn?: ListComparisonFn<T>): ListNode<T>[]

Removes all nodes with given value from the list:

  1. list.addMany(['a', 'x', 'b', 'x', 'c']).tail();
  2. // "a" <-> "x" <-> "b" <-> "x" <-> "c"
  3. list.drop().byValueAll('x');
  4. // "a" <-> "b" <-> "c"

You may pass a custom compare function to detect the searched value:

  1. list.addMany([{ x: 1 }, { x: 0 }, { x: 2 }, { x: 0 }, { x: 3 }]).tail();
  2. // {"x":1} <-> {"x":0} <-> {"x":2} <-> {"x":0} <-> {"x":3}
  3. list
  4. .drop()
  5. .byValueAll(0, (value, searchedValue) => value.x === searchedValue);
  6. // {"x":1} <-> {"x":2} <-> {"x":3}

This is an alternative API for dropByValueAll.

The default compare function checks deep equality, so you will rarely need to pass that parameter.

dropMany(count).head()

  1. dropMany(count: number).head(): ListNode<T>[]

Removes the first nodes from the list based on given count:

  1. list.addMany(['a', 'b', 'c']).tail();
  2. // "a" <-> "b" <-> "c"
  3. list.dropMany(2).head();
  4. // "c"

This is an alternative API for dropManyHead.

dropMany(count).tail()

  1. dropMany(count: number).tail(): ListNode<T>[]

Removes the last nodes from the list based on given count:

  1. list.addMany(['a', 'b', 'c']).tail();
  2. // "a" <-> "b" <-> "c"
  3. list.dropMany(2).tail();
  4. // "a"

This is an alternative API for dropManyTail.

dropMany(count).byIndex(position)

  1. dropMany(count: number).byIndex(position: number): ListNode<T>[]

Removes the nodes starting from the specified position from the list based on given count:

  1. list.addMany(['a', 'b', 'c', 'd']).tail();
  2. // "a" <-> "b" <-> "c" <-> "d
  3. list.dropMany(2).byIndex(1);
  4. // "a" <-> "d"

It works with negative index too:

  1. list.addMany(['a', 'b', 'c', 'd']).tail();
  2. // "a" <-> "b" <-> "c" <-> "d
  3. list.dropMany(2).byIndex(-2);
  4. // "a" <-> "d"

This is an alternative API for dropManyByIndex.

How to Find Nodes

There are a few methods to find specific nodes in a linked list.

head

  1. head: ListNode<T> | undefined;

Refers to the first node in the list.

tail

  1. tail: ListNode<T> | undefined;

Refers to the last node in the list.

length

  1. length: number;

Is the total number of nodes in the list.

find(predicate)

  1. find(predicate: ListIteratorFn<T>): ListNode<T> | undefined

Finds the first node from the list that matches the given predicate:

  1. list.addManyTail(['a', 'b', 'b', 'c']);
  2. // "a" <-> "b" <-> "b" <-> "c"
  3. var found = list.find(node => node.value === 'b');
  4. /*
  5. found.value === "b"
  6. found.previous.value === "a"
  7. found.next.value === "b"
  8. */

findIndex(predicate)

  1. findIndex(predicate: ListIteratorFn<T>): number

Finds the position of the first node from the list that matches the given predicate:

  1. list.addManyTail(['a', 'b', 'b', 'c']);
  2. // "a" <-> "b" <-> "b" <-> "c"
  3. var i0 = list.findIndex(node => node.next && node.next.value === 'b');
  4. var i1 = list.findIndex(node => node.value === 'b');
  5. var i2 = list.findIndex(node => node.previous && node.previous.value === 'b');
  6. var i3 = list.findIndex(node => node.value === 'x');
  7. /*
  8. i0 === 0
  9. i1 === 1
  10. i2 === 2
  11. i3 === -1
  12. */

get(position)

  1. get(position: number): ListNode<T> | undefined

Finds and returns the node with specific position in the list:

  1. list.addManyTail(['a', 'b', 'c']);
  2. // "a" <-> "b" <-> "c"
  3. var found = list.get(1);
  4. /*
  5. found.value === "b"
  6. found.previous.value === "a"
  7. found.next.value === "c"
  8. */

indexOf(value [, compareFn])

  1. indexOf(value: T, compareFn?: ListComparisonFn<T>): number

Finds the position of the first node from the list that has the given value:

  1. list.addManyTail(['a', 'b', 'b', 'c']);
  2. // "a" <-> "b" <-> "b" <-> "c"
  3. var i0 = list.indexOf('a');
  4. var i1 = list.indexOf('b');
  5. var i2 = list.indexOf('c');
  6. var i3 = list.indexOf('x');
  7. /*
  8. i0 === 0
  9. i1 === 1
  10. i2 === 3
  11. i3 === -1
  12. */

You may pass a custom compare function to detect the searched value:

  1. list.addManyTail([{ x: 1 }, { x: 0 }, { x: 2 }, { x: 0 }, { x: 3 }]);
  2. // {"x":1} <-> {"x":0} <-> {"x":2} <-> {"x":0} <-> {"x":3}
  3. var i0 = indexOf(1, (value, searchedValue) => value.x === searchedValue);
  4. var i1 = indexOf(2, (value, searchedValue) => value.x === searchedValue);
  5. var i2 = indexOf(3, (value, searchedValue) => value.x === searchedValue);
  6. var i3 = indexOf(0, (value, searchedValue) => value.x === searchedValue);
  7. var i4 = indexOf(4, (value, searchedValue) => value.x === searchedValue);
  8. /*
  9. i0 === 0
  10. i1 === 2
  11. i2 === 4
  12. i3 === 1
  13. i4 === -1
  14. */

The default compare function checks deep equality, so you will rarely need to pass that parameter.

How to Check All Nodes

There are a few ways to iterate over or display a linked list.

forEach(iteratorFn)

  1. forEach(iteratorFn: ListIteratorFn<T>): void

Runs a function on all nodes in a linked list from head to tail:

  1. list.addManyTail(['a', 'b', 'c']);
  2. // "a" <-> "b" <-> "c"
  3. list.forEach((node, index) => console.log(node.value + index));
  4. // 'a0'
  5. // 'b1'
  6. // 'c2'

*[Symbol.iterator]()

A linked list is iterable. In other words, you may use methods like for...of on it.

  1. list.addManyTail(['a', 'b', 'c']);
  2. // "a" <-> "b" <-> "c"
  3. for(const node of list) { /* ES6 for...of statement */
  4. console.log(node.value);
  5. }
  6. // 'a'
  7. // 'b'
  8. // 'c'

toArray()

  1. toArray(): T[]

Converts a linked list to an array of values:

  1. list.addManyTail(['a', 'b', 'c']);
  2. // "a" <-> "b" <-> "c"
  3. var arr = list.toArray();
  4. /*
  5. arr === ['a', 'b', 'c']
  6. */

toNodeArray()

  1. toNodeArray(): ListNode<T>[]

Converts a linked list to an array of nodes:

  1. list.addManyTail(['a', 'b', 'c']);
  2. // "a" <-> "b" <-> "c"
  3. var arr = list.toNodeArray();
  4. /*
  5. arr[0].value === 'a'
  6. arr[1].value === 'a'
  7. arr[2].value === 'a'
  8. */

toString([mapperFn])

  1. toString(mapperFn: ListMapperFn<T> = JSON.stringify): string

Converts a linked list to a string representation of nodes and their relations:

  1. list.addManyTail(['a', 2, 'c', { k: 4, v: 'd' }]);
  2. // "a" <-> 2 <-> "c" <-> {"k":4,"v":"d"}
  3. var str = list.toString();
  4. /*
  5. str === '"a" <-> 2 <-> "c" <-> {"k":4,"v":"d"}'
  6. */

You may pass a custom mapper function to map values before stringifying them:

  1. list.addMany([{ x: 1 }, { x: 2 }, { x: 3 }, { x: 4 }, { x: 5 }]).tail();
  2. // {"x":1} <-> {"x":2} <-> {"x":3} <-> {"x":4} <-> {"x":5}
  3. var str = list.toString(value => value.x);
  4. /*
  5. str === '1 <-> 2 <-> 3 <-> 4 <-> 5'
  6. */

API

Classes

LinkedList

  1. export class LinkedList<T = any> {
  2. // properties and methods are explained above
  3. }

ListNode

  1. export class ListNode<T = any> {
  2. next: ListNode | undefined;
  3. previous: ListNode | undefined;
  4. constructor(public readonly value: T) {}
  5. }

ListNode is the node that is being stored in the LinkedList for every record.

  • value is the value stored in the node and is passed through the constructor.
  • next refers to the next node in the list.
  • previous refers to the previous node in the list.
  1. list.addManyTail([ 0, 1, 2 ]);
  2. console.log(
  3. list.head.value, // 0
  4. list.head.next.value, // 1
  5. list.head.next.next.value, // 2
  6. list.head.next.next.previous.value, // 1
  7. list.head.next.next.previous.previous.value, // 0
  8. list.tail.value, // 2
  9. list.tail.previous.value, // 1
  10. list.tail.previous.previous.value, // 0
  11. list.tail.previous.previous.next.value, // 1
  12. list.tail.previous.previous.next.next.value, // 2
  13. );

Types

ListMapperFn

  1. type ListMapperFn<T = any> = (value: T) => any;

This function is used in toString method to map the node values before generating a string representation of the list.

ListComparisonFn

  1. type ListComparisonFn<T = any> = (nodeValue: T, comparedValue: any) => boolean;

This function is used while adding, dropping, ang finding nodes based on a comparison value.

ListIteratorFn

  1. type ListIteratorFn<T = any, R = boolean> = (
  2. node: ListNode<T>,
  3. index?: number,
  4. list?: LinkedList,
  5. ) => R;

This function is used while iterating over the list either to do something with each node or to find a node.