{"id":24612702,"url":"https://github.com/rfieve/doubly-linked-list","last_synced_at":"2025-06-24T10:37:28.233Z","repository":{"id":195610214,"uuid":"691625383","full_name":"rfieve/doubly-linked-list","owner":"rfieve","description":"A zero-dependency TypeScript library to work with double linked lists and arrays of any types.","archived":false,"fork":false,"pushed_at":"2024-07-24T10:07:30.000Z","size":350,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-05-19T20:51:07.001Z","etag":null,"topics":["array","data-structures","doubly-linked-list","typescript","utility-library"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rfieve.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-09-14T14:54:26.000Z","updated_at":"2024-07-24T10:07:34.000Z","dependencies_parsed_at":"2025-01-24T20:36:23.968Z","dependency_job_id":null,"html_url":"https://github.com/rfieve/doubly-linked-list","commit_stats":null,"previous_names":["rfieve/doubly-linked-list","rfieve/double-linked-list"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/rfieve/doubly-linked-list","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rfieve%2Fdoubly-linked-list","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rfieve%2Fdoubly-linked-list/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rfieve%2Fdoubly-linked-list/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rfieve%2Fdoubly-linked-list/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rfieve","download_url":"https://codeload.github.com/rfieve/doubly-linked-list/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rfieve%2Fdoubly-linked-list/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":261653050,"owners_count":23190379,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["array","data-structures","doubly-linked-list","typescript","utility-library"],"created_at":"2025-01-24T20:25:04.045Z","updated_at":"2025-06-24T10:37:28.186Z","avatar_url":"https://github.com/rfieve.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ✌️🔗📝 doubly-linked-list\n\nA zero-dependency TypeScript library to work with doubly linked lists and arrays of any types.\n\n## Table of Content\n\n-   [✌️🔗📝 doubly-linked-list](#️-doubly-linked-list)\n    -   [Table of Content](#table-of-content)\n    -   [Installation](#installation)\n    -   [Usage](#usage)\n    -   [Documentation](#documentation)\n        -   [`toDLL`](#todll)\n        -   [`unshift`, `insert`, `push`](#unshift-insert-push)\n        -   [`shift`, `remove`, `pop`](#shift-remove-pop)\n        -   [`sort`](#sort)\n        -   [`map`](#map)\n        -   [`reduce`](#reduce)\n        -   [`findOne`, `findMany`](#findone-findmany)\n        -   [`find(Gt/Gte/Lt/Lte)`](#findgtgteltlte)\n        -   [`traverse`](#traverse)\n        -   [`toArray`](#toarray)\n        -   [`hasNodes`, `hasPrev`, `hasNext`](#hasnodes-hasprev-hasnext)\n        -   [`makeCompareUtils`](#makecompareutils)\n        -   [The infamous `DoublyLinkedList` class](#the-infamous-doublylinkedlist-class)\n\n## Installation\n\n```sh\nyarn add @romainfieve/doubly-linked-list\n```\n\nor\n\n```sh\nnpm install @romainfieve/doubly-linked-list\n```\n\n## Usage\n\n```typescript\ntype Hero = { name: string };\n\nconst compareAlpha = (a: Hero, b: Hero) =\u003e a.name.localeCompare(b.name);\n\nconst insertAlpha = makeInsert(compareAlpha);\nconst removeAlpha = makeRemove(compareAlpha);\nconst findOneAlpha = makeFindOne(compareAlpha);\n\nconst heroes: Hero[] = [\n    { name: 'Han' },\n    { name: 'Anakin' },\n    { name: 'Leia' },\n    { name: 'Luke' },\n    { name: 'Padme' },\n    { name: 'Lando' },\n    { name: 'Chewie' },\n];\n\nconst list = toDLL(heroes, compareAlpha);\n\n// Schema of \"list\"\n// Anakin \u003c-\u003e Chewie \u003c-\u003e Han \u003c-\u003e Lando \u003c-\u003e Leia \u003c-\u003e Luke \u003c-\u003e Padme\n\nconst updatedList = pipe(\n    (t) =\u003e insertAlpha(t, { name: 'Obiwan' }),\n    (t) =\u003e insertAlpha(t, [{ name: 'Boba' }, { name: 'Grogu' }]),\n    (t) =\u003e push(t, { name: 'Vador' }),\n    (t) =\u003e removeAlpha(t, [{ name: 'Han' }, { name: 'Padme' }]),\n    (t) =\u003e removeAlpha(t, { name: 'Luke' })\n)(list);\n\n// Schema of \"updatedList\"\n// Anakin \u003c-\u003e Boba \u003c-\u003e Chewie \u003c-\u003e Grogu \u003c-\u003e Lando \u003c-\u003e Leia \u003c-\u003e Obiwan \u003c-\u003e Vador\n\nconst grogu = findOneAlpha(updatedList, { name: 'Grogu' }); // { data: 'Grogu', next: ..., prev: ...}\n```\n\n## Documentation\n\n### `toDLL`\n\nConverts the given array to a doubly linked list (`toDLL`), depending on a given compare function if provided.\n\n```typescript\nconst arr = [10, 32, 13, 2, 89, 5, 50];\nconst compare = (a: number, b: number) =\u003e a - b;\n\nconst list = toDLL(arr, compare);\n// Schema of \"list\"\n// 2 \u003c-\u003e 5 \u003c-\u003e 10 \u003c-\u003e 13 \u003c-\u003e 32 \u003c-\u003e 50 \u003c-\u003e 89\n\nconst unorderedList = toDLL(arr);\n// Schema of \"unorderedList\"\n// 10 \u003c-\u003e 32 \u003c-\u003e 13 \u003c-\u003e 2 \u003c-\u003e 89 \u003c-\u003e 5 \u003c-\u003e 50\n```\n\n---\n\n### `unshift`, `insert`, `push`\n\nInserts a (or list of) given node(s) to the given doubly linked list (in place) and returns the list.\n\n-   with the given compare function (`insert`)\n-   at the head (`unshift`)\n-   at the tail (`push`)\n\n\u003e :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.\n\n```typescript\n// Schema of \"list\"\n// 2 \u003c-\u003e 5 \u003c-\u003e 10 \u003c-\u003e 13 \u003c-\u003e 32 \u003c-\u003e 50 \u003c-\u003e 89\n\nconst a = insert(list, 11, compare);\n// 2 \u003c-\u003e 5 \u003c-\u003e 10 \u003c-\u003e 11 \u003c-\u003e 13 \u003c-\u003e 32 \u003c-\u003e 50 \u003c-\u003e 89\nconst b = insert(list, [1, 100], compare);\n// 1 \u003c-\u003e 2 \u003c-\u003e 5 \u003c-\u003e 10 \u003c-\u003e 13 \u003c-\u003e 32 \u003c-\u003e 50 \u003c-\u003e 89 \u003c-\u003e 100\nconst c = push(list, [3, 17]);\n// 1 \u003c-\u003e 2 \u003c-\u003e 5 \u003c-\u003e 10 \u003c-\u003e 13 \u003c-\u003e 32 \u003c-\u003e 50 \u003c-\u003e 89 \u003c-\u003e 100 \u003c-\u003e 3 \u003c-\u003e 17\nconst d = unshift(list, 7);\n// 7 \u003c-\u003e 1 \u003c-\u003e 2 \u003c-\u003e 5 \u003c-\u003e 10 \u003c-\u003e 13 \u003c-\u003e 32 \u003c-\u003e 50 \u003c-\u003e 89 \u003c-\u003e 100 \u003c-\u003e 3 \u003c-\u003e 17\n```\n\n---\n\n### `shift`, `remove`, `pop`\n\nRemoves a (or list of) given node(s) from the given doubly linked list (in place) with the given compare function and returns the list.\n\n-   with the given compare function (`remove`)\n-   at the head (`shift`)\n-   at the tail (`pop`)\n\n```typescript\n// Schema of \"list\"\n// 2 \u003c-\u003e 5 \u003c-\u003e 10 \u003c-\u003e 13 \u003c-\u003e 32 \u003c-\u003e 50 \u003c-\u003e 89\n\nconst a = remove(list, 13, compare);\n// 2 \u003c-\u003e 5 \u003c-\u003e 10 \u003c-\u003e 32 \u003c-\u003e 50 \u003c-\u003e 89\nconst b = remove(list, [2, 89], compare);\n// 5 \u003c-\u003e 10 \u003c-\u003e 32 \u003c-\u003e 50\nconst c = shift(list);\n// 10 \u003c-\u003e 32 \u003c-\u003e 50\nconst d = pop(list);\n// 10 \u003c-\u003e 32\n```\n\n---\n\n### `sort`\n\nSorts a doubly linked list with the given compare function and returns the new list.\n\n```typescript\n// Schema of \"list\"\n// 2 \u003c-\u003e 5 \u003c-\u003e 10 \u003c-\u003e 13 \u003c-\u003e 32 \u003c-\u003e 50 \u003c-\u003e 89\n\nconst reversed = sort(list, (a: number, b: number) =\u003e b - a);\n// 89 \u003c-\u003e 50 \u003c-\u003e 32 \u003c-\u003e 13 \u003c-\u003e 10 \u003c-\u003e 5 \u003c-\u003e 2\nconst ordered = sort(reversed, (a: number, b: number) =\u003e a - b);\n// 2 \u003c-\u003e 5 \u003c-\u003e 10 \u003c-\u003e 13 \u003c-\u003e 32 \u003c-\u003e 50 \u003c-\u003e 89\n```\n\n---\n\n### `map`\n\nMaps the given doubly linked list nodes' data to anything.\n\n```typescript\n// Schema of \"list\"\n// 2 \u003c-\u003e 5 \u003c-\u003e 10 \u003c-\u003e 13 \u003c-\u003e 32 \u003c-\u003e 50 \u003c-\u003e 89\n\nconst mapper = (node: DLLNode, index: number) =\u003e `${node.data}[${index}]`;\nconst a = map(list, mapper);\n// '2[0]' \u003c-\u003e '5[1]' \u003c-\u003e '10[2]' \u003c-\u003e '13[3]' \u003c-\u003e '32[4]' \u003c-\u003e '50[5]' \u003c-\u003e '89[6]'\n```\n\n---\n\n### `reduce`\n\nReduces the given doubly linked list to anything.\n\n```typescript\n// Schema of \"list\"\n// 2 \u003c-\u003e 5 \u003c-\u003e 10 \u003c-\u003e 13 \u003c-\u003e 32 \u003c-\u003e 50 \u003c-\u003e 89\n\nconst reducer = (acc: number, node: DLLNode, index: number) =\u003e acc + node.data + index;\nconst a = reduce(list, reducer, 0); // 222\n```\n\n---\n\n### `findOne`, `findMany`\n\nFinds the first (`findOne`) or all (`findMany`) the matching node(s) into the given doubly linked list with the given compare function.\n\n```typescript\n// This compare function will capture the elements that, when compared with the searched one,\n// will be in range of x - 5 to x + 5.\nconst compare = (a: number, b: number) =\u003e {\n    if (a \u003e b + 5) {\n        return 1;\n    }\n    if (a \u003c b - 5) {\n        return -1;\n    }\n    return 0;\n};\n\n// Schema of \"list\"\n// 2 \u003c-\u003e 5 \u003c-\u003e 10 \u003c-\u003e 13 \u003c-\u003e 32 \u003c-\u003e 50 \u003c-\u003e 89\n\nconst node = findOne(list, compare, 12)?.data; // 10\nconst nodes = findMany(list, compare, 12).map(({ data }) =\u003e data); // [ 10, 13 ]\n```\n\n---\n\n### `find(Gt/Gte/Lt/Lte)`\n\nFinds all gt/gte/lt/lte nodes into the given doubly linked list with the given compare function.\n\n-   `findGt`\n-   `findGte`\n-   `findLt`\n-   `findLte`\n\n```typescript\n// Schema of \"list\"\n// 2 \u003c-\u003e 5 \u003c-\u003e 10 \u003c-\u003e 13 \u003c-\u003e 32 \u003c-\u003e 50 \u003c-\u003e 89\n\nconst results = findGte(list, compare, 13).map(({ data }) =\u003e data);\n// [13, 32, 50, 89]\n```\n\n---\n\n### `traverse`\n\nTraverses a doubly linked list, invoking the callback function on each visited node:\n\n-   `traverseFrom`\n-   `traverseInOrder`\n-   `traverseInOrderReverse`\n\n```typescript\n// Schema of \"list\"\n// 2 \u003c-\u003e 5 \u003c-\u003e 10 \u003c-\u003e 13 \u003c-\u003e 32 \u003c-\u003e 50 \u003c-\u003e 89\n\nconst collect = (collection: number[]) =\u003e (node: { data: number }) =\u003e {\n    collection.push(node.data);\n};\n\nconst elements = [];\n\ntraverseFrom(list.head, 'next', collect(elements));\n// elements: [2, 5, 10, 13, 32, 50, 89]\ntraverseInOrder(list, collect(elements));\n// elements: [2, 5, 10, 13, 32, 50, 89]\ntraverseInOrderReverse(list, collect(elements));\n// elements: [89, 50, 32, 13, 10, 5, 2]\n```\n\n---\n\n### `toArray`\n\nConverts the given doubly linked list to an array sorted as traversed, with an optional mapper:\n\n-   `toArrayInOrder`\n-   `toArrayInOrderReverse`\n-   `toArrayMapInOrder`\n-   `toArrayMapInOrderReverse`\n\n```typescript\n// Schema of \"list\"\n// 2 \u003c-\u003e 5 \u003c-\u003e 10 \u003c-\u003e 13 \u003c-\u003e 32 \u003c-\u003e 50 \u003c-\u003e 89\n\nconst a = toArrayInOrder(list);\n// [2, 5, 10, 13, 32, 50, 89]\nconst b = toArrayInOrderReverse(list);\n// [89, 50, 32, 13, 10, 5, 2]\n\nconst mapper = (node: DLLNode, index: number) =\u003e node.data * index;\nconst c = toArrayMapInOrder(list, mapper);\n// [0, 5, 20, 39, 128, 250, 534]\n```\n\n---\n\n### `hasNodes`, `hasPrev`, `hasNext`\n\nAssesses if the list contains nodes `hasNodes`.\nAssesses if the given node has a prev node (`hasPrev`) or a next node (`hasNext`).\n\n```typescript\n// Schema of \"list\"\n// 2 \u003c-\u003e 5 \u003c-\u003e 10 \u003c-\u003e 13 \u003c-\u003e 32 \u003c-\u003e 50 \u003c-\u003e 89\n\nconst isNonEmpty = hasNodes(list); // true\n\nconst hasPrevA = hasPrev(list.tail); // true\nconst hasPrevB = hasPrev(list.head); // false\n\nconst hasNextA = hasNext(list.head); // true\nconst hasNextB = hasNext(list.tail); // false\n```\n\n---\n\n### `makeCompareUtils`\n\nAs 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.\n\n```typescript\n// compare-alpha.ts\nexport const compareAlpha = (a: Hero, b: Hero) =\u003e a.name.localeCompare(b.name);\nexport const {\n    toDLL: toDLLAlpha,\n    insert: insertAlpha,\n    remove: removeAlpha,\n    sort: sortAlpha,\n    findOne: findOneAlpha,\n    findMany: findManyAlpha,\n    findGt: findGtAlpha,\n    findGte: findGteAlpha,\n    findLt: findLtAlpha,\n    findLte: findLteAlpha,\n} = makeCompareUtils(compareAlpha);\n\n// other-file.ts\nimport {\n    toDLLAlpha,\n    insertAlpha,\n    removeAlpha,\n    sortAlpha,\n    findOneAlpha,\n    findManyAlpha,\n    findGtAlpha,\n    findGteAlpha,\n    findLtAlpha,\n    findLteAlpha,\n} from './compare-alpha';\n\nconst list = toDLLAlpha([{ name: 'Anakin' }]);\n\nconst updatedList = pipe(\n    (t) =\u003e insertAlpha(t, { name: 'Yoda' }),\n    (t) =\u003e removeAlpha(t, { name: 'Anakin' }),\n    (t) =\u003e findGteAlpha({ name: 'Yoda' })\n)(list); // [{ data: 'Yoda' }]\n```\n\n---\n\n### The infamous `DoublyLinkedList` class\n\nWhile diverging from the functional approach, the `DoublyLinkedList` class offers many advantages, depending on the situation:\n\nPros:\n\n-   Natural chaining\n-   List state encapsulation\n-   Compare function encapsulation\n-   Has all methods listed as functions before\n\nCons:\n\n-   No tree shaking of unused methods, obviously\n\nLet's rewrite the Star Wars example with this approach:\n\n```typescript\ntype Hero = { name: string };\n\nconst compareAlpha = (a: Hero, b: Hero) =\u003e a.name.localeCompare(b.name);\n\nconst heroes: Hero[] = [\n    { name: 'Han' },\n    { name: 'Anakin' },\n    { name: 'Leia' },\n    { name: 'Luke' },\n    { name: 'Padme' },\n    { name: 'Lando' },\n    { name: 'Chewie' },\n];\n\nconst list = new DoublyLinkedList(heroes, compareAlpha);\n// Schema of list.list\n// Anakin \u003c-\u003e Chewie \u003c-\u003e Han \u003c-\u003e Lando \u003c-\u003e Leia \u003c-\u003e Luke \u003c-\u003e Padme\n\nlist.insert({ name: 'Yoda' })\n    .insert({ name: 'Obiwan' })\n    .insert([{ name: 'Boba' }, { name: 'Grogu' }])\n    .push({ name: 'Vador' })\n    .remove([{ name: 'Han' }, { name: 'Padme' }])\n    .remove({ name: 'Luke' });\n\n// Schema of list.list, after update\n// Anakin \u003c-\u003e Boba \u003c-\u003e Chewie \u003c-\u003e Grogu \u003c-\u003e Lando \u003c-\u003e Leia \u003c-\u003e Obiwan \u003c-\u003e Yoda \u003c-\u003e Vador\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frfieve%2Fdoubly-linked-list","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frfieve%2Fdoubly-linked-list","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frfieve%2Fdoubly-linked-list/lists"}