npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@romainfieve/doubly-linked-list

v3.1.4

Published

A zero-dependency TypeScript library to work with doubly linked lists and arrays of any types.

Downloads

4

Readme

✌️🔗📝 doubly-linked-list

A zero-dependency TypeScript library to work with doubly linked lists and arrays of any types.

Table of Content

Installation

yarn add @romainfieve/doubly-linked-list

or

npm install @romainfieve/doubly-linked-list

Usage

type Hero = { name: string };

const compareAlpha = (a: Hero, b: Hero) => a.name.localeCompare(b.name);

const insertAlpha = makeInsert(compareAlpha);
const removeAlpha = makeRemove(compareAlpha);
const findOneAlpha = makeFindOne(compareAlpha);

const heroes: Hero[] = [
    { name: 'Han' },
    { name: 'Anakin' },
    { name: 'Leia' },
    { name: 'Luke' },
    { name: 'Padme' },
    { name: 'Lando' },
    { name: 'Chewie' },
];

const list = toDLL(heroes, compareAlpha);

// Schema of "list"
// Anakin <-> Chewie <-> Han <-> Lando <-> Leia <-> Luke <-> Padme

const updatedList = pipe(
    (t) => insertAlpha(t, { name: 'Obiwan' }),
    (t) => insertAlpha(t, [{ name: 'Boba' }, { name: 'Grogu' }]),
    (t) => push(t, { name: 'Vador' }),
    (t) => removeAlpha(t, [{ name: 'Han' }, { name: 'Padme' }]),
    (t) => removeAlpha(t, { name: 'Luke' })
)(list);

// Schema of "updatedList"
// Anakin <-> Boba <-> Chewie <-> Grogu <-> Lando <-> Leia <-> Obiwan <-> Vador

const grogu = findOneAlpha(updatedList, { name: 'Grogu' }); // { data: 'Grogu', next: ..., prev: ...}

Documentation

toDLL

Converts the given array to a doubly linked list (toDLL), depending on a given compare function if provided.

const arr = [10, 32, 13, 2, 89, 5, 50];
const compare = (a: number, b: number) => a - b;

const list = toDLL(arr, compare);
// Schema of "list"
// 2 <-> 5 <-> 10 <-> 13 <-> 32 <-> 50 <-> 89

const unorderedList = toDLL(arr);
// Schema of "unorderedList"
// 10 <-> 32 <-> 13 <-> 2 <-> 89 <-> 5 <-> 50

unshift, insert, push

Inserts a (or list of) given node(s) to the given doubly linked list (in place) and returns the list.

  • with the given compare function (insert)
  • at the head (unshift)
  • at the tail (push)

:warning: Using another compare function than the one used to create the list with toDLL or using push/unshift will of course f**k up the sorting. A safer approach consists of using makeInsert. It curries an insert closure function with the given compare function.

// Schema of "list"
// 2 <-> 5 <-> 10 <-> 13 <-> 32 <-> 50 <-> 89

const a = insert(list, 11, compare);
// 2 <-> 5 <-> 10 <-> 11 <-> 13 <-> 32 <-> 50 <-> 89
const b = insert(list, [1, 100], compare);
// 1 <-> 2 <-> 5 <-> 10 <-> 13 <-> 32 <-> 50 <-> 89 <-> 100
const c = push(list, [3, 17]);
// 1 <-> 2 <-> 5 <-> 10 <-> 13 <-> 32 <-> 50 <-> 89 <-> 100 <-> 3 <-> 17
const d = unshift(list, 7);
// 7 <-> 1 <-> 2 <-> 5 <-> 10 <-> 13 <-> 32 <-> 50 <-> 89 <-> 100 <-> 3 <-> 17

shift, remove, pop

Removes a (or list of) given node(s) from the given doubly linked list (in place) with the given compare function and returns the list.

  • with the given compare function (remove)
  • at the head (shift)
  • at the tail (pop)
// Schema of "list"
// 2 <-> 5 <-> 10 <-> 13 <-> 32 <-> 50 <-> 89

const a = remove(list, 13, compare);
// 2 <-> 5 <-> 10 <-> 32 <-> 50 <-> 89
const b = remove(list, [2, 89], compare);
// 5 <-> 10 <-> 32 <-> 50
const c = shift(list);
// 10 <-> 32 <-> 50
const d = pop(list);
// 10 <-> 32

sort

Sorts a doubly linked list with the given compare function and returns the new list.

// Schema of "list"
// 2 <-> 5 <-> 10 <-> 13 <-> 32 <-> 50 <-> 89

const reversed = sort(list, (a: number, b: number) => b - a);
// 89 <-> 50 <-> 32 <-> 13 <-> 10 <-> 5 <-> 2
const ordered = sort(reversed, (a: number, b: number) => a - b);
// 2 <-> 5 <-> 10 <-> 13 <-> 32 <-> 50 <-> 89

map

Maps the given doubly linked list nodes' data to anything.

// Schema of "list"
// 2 <-> 5 <-> 10 <-> 13 <-> 32 <-> 50 <-> 89

const mapper = (node: DLLNode, index: number) => `${node.data}[${index}]`;
const a = map(list, mapper);
// '2[0]' <-> '5[1]' <-> '10[2]' <-> '13[3]' <-> '32[4]' <-> '50[5]' <-> '89[6]'

reduce

Reduces the given doubly linked list to anything.

// Schema of "list"
// 2 <-> 5 <-> 10 <-> 13 <-> 32 <-> 50 <-> 89

const reducer = (acc: number, node: DLLNode, index: number) => acc + node.data + index;
const a = reduce(list, reducer, 0); // 222

findOne, findMany

Finds the first (findOne) or all (findMany) the matching node(s) into the given doubly linked list with the given compare function.

// This compare function will capture the elements that, when compared with the searched one,
// will be in range of x - 5 to x + 5.
const compare = (a: number, b: number) => {
    if (a > b + 5) {
        return 1;
    }
    if (a < b - 5) {
        return -1;
    }
    return 0;
};

// Schema of "list"
// 2 <-> 5 <-> 10 <-> 13 <-> 32 <-> 50 <-> 89

const node = findOne(list, compare, 12)?.data; // 10
const nodes = findMany(list, compare, 12).map(({ data }) => data); // [ 10, 13 ]

find(Gt/Gte/Lt/Lte)

Finds all gt/gte/lt/lte nodes into the given doubly linked list with the given compare function.

  • findGt
  • findGte
  • findLt
  • findLte
// Schema of "list"
// 2 <-> 5 <-> 10 <-> 13 <-> 32 <-> 50 <-> 89

const results = findGte(list, compare, 13).map(({ data }) => data);
// [13, 32, 50, 89]

traverse

Traverses a doubly linked list, invoking the callback function on each visited node:

  • traverseFrom
  • traverseInOrder
  • traverseInOrderReverse
// Schema of "list"
// 2 <-> 5 <-> 10 <-> 13 <-> 32 <-> 50 <-> 89

const collect = (collection: number[]) => (node: { data: number }) => {
    collection.push(node.data);
};

const elements = [];

traverseFrom(list.head, 'next', collect(elements));
// elements: [2, 5, 10, 13, 32, 50, 89]
traverseInOrder(list, collect(elements));
// elements: [2, 5, 10, 13, 32, 50, 89]
traverseInOrderReverse(list, collect(elements));
// elements: [89, 50, 32, 13, 10, 5, 2]

toArray

Converts the given doubly linked list to an array sorted as traversed, with an optional mapper:

  • toArrayInOrder
  • toArrayInOrderReverse
  • toArrayMapInOrder
  • toArrayMapInOrderReverse
// Schema of "list"
// 2 <-> 5 <-> 10 <-> 13 <-> 32 <-> 50 <-> 89

const a = toArrayInOrder(list);
// [2, 5, 10, 13, 32, 50, 89]
const b = toArrayInOrderReverse(list);
// [89, 50, 32, 13, 10, 5, 2]

const mapper = (node: DLLNode, index: number) => node.data * index;
const c = toArrayMapInOrder(list, mapper);
// [0, 5, 20, 39, 128, 250, 534]

hasNodes, hasPrev, hasNext

Assesses if the list contains nodes hasNodes. Assesses if the given node has a prev node (hasPrev) or a next node (hasNext).

// Schema of "list"
// 2 <-> 5 <-> 10 <-> 13 <-> 32 <-> 50 <-> 89

const isNonEmpty = hasNodes(list); // true

const hasPrevA = hasPrev(list.tail); // true
const hasPrevB = hasPrev(list.head); // false

const hasNextA = hasNext(list.head); // true
const hasNextB = hasNext(list.tail); // false

makeCompareUtils

As the compare function is centric, for both the creation and the traversals of the DLL, a good practice is to create all the necessary utils, along with it. This will be DRY and ensure reusability and consistency.

// compare-alpha.ts
export const compareAlpha = (a: Hero, b: Hero) => a.name.localeCompare(b.name);
export const {
    toDLL: toDLLAlpha,
    insert: insertAlpha,
    remove: removeAlpha,
    sort: sortAlpha,
    findOne: findOneAlpha,
    findMany: findManyAlpha,
    findGt: findGtAlpha,
    findGte: findGteAlpha,
    findLt: findLtAlpha,
    findLte: findLteAlpha,
} = makeCompareUtils(compareAlpha);

// other-file.ts
import {
    toDLLAlpha,
    insertAlpha,
    removeAlpha,
    sortAlpha,
    findOneAlpha,
    findManyAlpha,
    findGtAlpha,
    findGteAlpha,
    findLtAlpha,
    findLteAlpha,
} from './compare-alpha';

const list = toDLLAlpha([{ name: 'Anakin' }]);

const updatedList = pipe(
    (t) => insertAlpha(t, { name: 'Yoda' }),
    (t) => removeAlpha(t, { name: 'Anakin' }),
    (t) => findGteAlpha({ name: 'Yoda' })
)(list); // [{ data: 'Yoda' }]

The infamous DoublyLinkedList class

While diverging from the functional approach, the DoublyLinkedList class offers many advantages, depending on the situation:

Pros:

  • Natural chaining
  • List state encapsulation
  • Compare function encapsulation
  • Has all methods listed as functions before

Cons:

  • No tree shaking of unused methods, obviously

Let's rewrite the Star Wars example with this approach:

type Hero = { name: string };

const compareAlpha = (a: Hero, b: Hero) => a.name.localeCompare(b.name);

const heroes: Hero[] = [
    { name: 'Han' },
    { name: 'Anakin' },
    { name: 'Leia' },
    { name: 'Luke' },
    { name: 'Padme' },
    { name: 'Lando' },
    { name: 'Chewie' },
];

const list = new DoublyLinkedList(heroes, compareAlpha);
// Schema of list.list
// Anakin <-> Chewie <-> Han <-> Lando <-> Leia <-> Luke <-> Padme

list.insert({ name: 'Yoda' })
    .insert({ name: 'Obiwan' })
    .insert([{ name: 'Boba' }, { name: 'Grogu' }])
    .push({ name: 'Vador' })
    .remove([{ name: 'Han' }, { name: 'Padme' }])
    .remove({ name: 'Luke' });

// Schema of list.list, after update
// Anakin <-> Boba <-> Chewie <-> Grogu <-> Lando <-> Leia <-> Obiwan <-> Yoda <-> Vador