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 🙏

© 2025 – Pkg Stats / Ryan Hefner

doublell

v2.0.1

Published

A doubly linked list

Readme

doublell

Downloads Size TypeScript MIT License

The most type-safe, performant, and developer-friendly doubly linked list for TypeScript and JavaScript

A production-ready doubly linked list implementation that works seamlessly in both browsers and Node.js. Combines the performance of low-level data structures with the convenience of modern JavaScript APIs. Perfect for algorithms, data processing, and any application requiring efficient insertion/deletion operations.

🚀 Quick Start

npm install doublell
# or
yarn add doublell
import { DoubleLinkedList } from 'doublell';

// Create and populate
const list = new DoubleLinkedList(1, 2, 3);

// Modern iteration
for (const item of list) {
  console.log(item); // 1, 2, 3
}

// Array-like methods
const doubled = list.map((x) => x * 2);
console.log([...doubled]); // [2, 4, 6]

// Stack/Queue operations
list.push(4);           // Add to end
const last = list.pop(); // Remove from end
const first = list.shift(); // Remove from start

✨ Why Choose doublell?

🎯 Type-Safe by Design

// Full TypeScript generics support
const numbers = new DoubleLinkedList<number>(1, 2, 3);
const strings = numbers.map((n) => n.toString()); // DoubleLinkedList<string>

// AI assistants and IDEs love the complete type information
const found = list.find((x) => x > 5); // number | undefined

Performance Optimized

  • O(1) insertion/deletion at any position (when you have the node reference)
  • O(1) push/pop/shift/unshift operations
  • Zero dependencies - minimal bundle impact for web and Node.js

🧑‍💻 Universal Developer Experience

// Array-like index access with optimization
console.log(list.get(0));    // First item
console.log(list.get(-1));   // Last item (optimized from tail)
console.log(list.get(5));    // Item at index 5

// Familiar Array-like API
list.forEach((item, index) => console.log(item, index));
const found = list.find((x) => x > 10);
const includes = list.includes(42);
const index = list.indexOf('hello');

// Iterator protocol support
const array = [...list];              // Spread operator
const mapped = Array.from(list);      // Array.from
for (const item of list) { /* */ }    // for...of loops

// Stack and Queue operations (works in browser and Node.js)
const stack = new DoubleLinkedList<string>();
stack.push('first'); stack.push('second');
console.log(stack.pop()); // 'second' (LIFO)

const queue = new DoubleLinkedList<number>();
queue.push(1); queue.push(2);
console.log(queue.shift()); // 1 (FIFO)

🔧 Node-Level Control

import { DoubleLinkedList, getNextNode, getPreviousNode, getNodeValue } from 'doublell';

// Traditional node manipulation
const list = new DoubleLinkedList('a', 'b', 'd');
const nodeB = getNextNode(list.getHead());
if (nodeB) {
  list.insertAfterNode(nodeB, 'c'); // O(1) insertion
}

// Enhanced node-based splice - even more powerful!
const list2 = new DoubleLinkedList(1, 2, 5, 6);
const nodeAt5 = getPreviousNode(list2.getTail());
if (nodeAt5) {
  // Replace one item with multiple - all in O(1)!
  list2.splice(nodeAt5, 1, 3, 4); // [1, 2, 3, 4, 6]
}

// Append efficiently using undefined
list2.splice(undefined, 0, 7, 8, 9); // [1, 2, 3, 4, 6, 7, 8, 9]

📚 Complete API Reference

Core Operations

// Creation and basic operations
const list = new DoubleLinkedList<T>(...items);
list.append(item)           // Add to end
list.prepend(item)          // Add to beginning  
list.getLength()            // Get size
list.isEmpty()              // Check if empty
list.clear()                // Remove all items
list.remove(node)           // Remove specific node

// Node access
list.getHead()              // First node
list.getTail()              // Last node
list.insertAfterNode(node, item)   // Insert after node
list.insertBeforeNode(node, item)  // Insert before node

Array-like Methods

// Index-based access
list.get(index)             // Get item by index (supports negative indices)
list.getNodeAt(index)       // Get node by index (supports negative indices)

// Iteration and transformation
list.forEach(callback)      // Execute function for each item
list.map(callback)          // Transform to new list
list.find(predicate)        // Find first matching item
list.indexOf(item)          // Get index of item
list.includes(item)         // Check if item exists
list.toArray()              // Convert to array

// Array modification
list.splice(start, deleteCount, ...items)  // Remove/insert items at index

// Stack/Queue operations  
list.push(item)             // Add to end (alias for append)
list.pop()                  // Remove from end
list.shift()                // Remove from beginning
list.unshift(item)          // Add to beginning (alias for prepend)

// Iterator support
[...list]                   // Spread to array
Array.from(list)            // Convert to array
for (const item of list)    // for...of iteration

🎨 Real-World Use Cases

LRU Cache Implementation

// Why DoubleLinkedList is perfect for LRU Cache:
// - O(1) access to both ends (most/least recently used)
// - O(1) removal from anywhere (when you have the node reference)  
// - O(1) insertion at head (mark as most recently used)

class LRUCache<K, V> {
  private capacity: number;
  private list = new DoubleLinkedList<{ key: K; value: V }>();
  private map = new Map<K, DoubleLinkedListNode<{ key: K; value: V }>>();

  constructor(capacity: number) {
    this.capacity = capacity;
  }

  get(key: K): V | undefined {
    const node = this.map.get(key);
    if (node !== undefined) {
      // Move to front (most recently used) - O(1)!
      this.list.remove(node);
      const nodeValue = getNodeValue(node);
      const newNode = this.list.prepend(nodeValue);
      this.map.set(key, newNode);
      return nodeValue.value;
    }
    return undefined;
  }

  set(key: K, value: V): void {
    const existingNode = this.map.get(key);
    
    if (existingNode !== undefined) {
      // Update existing - move to front
      this.list.remove(existingNode);
      const newNode = this.list.prepend({ key, value });
      this.map.set(key, newNode);
    } else {
      // Check capacity - evict LRU if needed
      if (this.list.getLength() >= this.capacity) {
        const tail = this.list.getTail(); // Least recently used
        if (tail !== undefined) {
          this.list.remove(tail);        // O(1) eviction!
          const tailValue = getNodeValue(tail);
          this.map.delete(tailValue.key);
        }
      }
      
      // Add new item at front (most recently used)
      const newNode = this.list.prepend({ key, value });
      this.map.set(key, newNode);
    }
  }
}

// Perfect O(1) performance for all LRU operations!
const cache = new LRUCache<string, number>(3);
cache.set('a', 1); cache.set('b', 2); cache.set('c', 3);
cache.get('a'); // Move 'a' to front
cache.set('d', 4); // Evicts 'b' (LRU) in O(1)

Undo/Redo System

import { DoubleLinkedList, DoubleLinkedListNode, getNextNode, getPreviousNode, getNodeValue } from 'doublell';

class UndoRedoManager<T> {
  private history = new DoubleLinkedList<T>();
  private current: DoubleLinkedListNode<T> | undefined;
  
  execute(state: T): void {
    // When executing new action, discard any "future" states using node-based splice
    const nextNode = this.current ? getNextNode(this.current) : undefined;
    if (this.current !== undefined && nextNode !== undefined) {
      // Remove all states after current position - O(1) with node reference!
      this.history.splice(nextNode, this.history.getLength());
    }
    
    // Add new state
    this.current = this.current !== undefined ? 
      this.history.insertAfterNode(this.current, state) : 
      this.history.append(state);
  }
  
  undo(): T | undefined {
    const prevNode = this.current ? getPreviousNode(this.current) : undefined;
    if (this.current !== undefined && prevNode !== undefined) {
      this.current = prevNode;
      return getNodeValue(this.current);
    }
    return undefined;
  }
  
  redo(): T | undefined {
    const nextNode = this.current ? getNextNode(this.current) : undefined;
    if (this.current !== undefined && nextNode !== undefined) {
      this.current = nextNode;
      return getNodeValue(this.current);
    }
    return undefined;
  }
}

🤖 AI Assistant Friendly

This library is designed to work seamlessly with AI coding assistants:

// Rich type information helps AI understand your intent
const userList = new DoubleLinkedList<User>();

// AI can suggest appropriate methods based on context
userList.find((user) => user.isActive);     // AI knows this returns User | undefined
userList.map((user) => user.email);         // AI knows this returns DoubleLinkedList<string>
userList.forEach((user, index) => {       // AI provides correct callback signature
  console.log(`User ${index}: ${user.name}`);
});

// Comprehensive JSDoc comments provide context for AI suggestions
// AI assistants can explain performance characteristics and usage patterns

📊 Performance Comparison

| Operation | doublell | Array | Native Set | |-----------|----------|-------|------------| | Insert at position | O(1) ⚡ | O(n) | N/A | | Delete at position | O(1) ⚡ | O(n) | N/A | | Push/Pop | O(1) ⚡ | O(1) ⚡ | N/A | | Shift/Unshift | O(1) ⚡ | O(n) | N/A | | Find element | O(n) | O(n) | O(1) ⚡ | | Iteration | O(n) | O(n) | O(n) |

Use doublell when you need frequent insertions/deletions. Use Array for random access. Use Set for fast lookups.

🛠️ Development

# Install dependencies
yarn install

# Run tests (Node.js)
yarn test

# Build for production (CommonJS + ESM)
yarn build

# Generate documentation
yarn generate:docs

# Lint code
yarn lint

Browser Support

  • Modern browsers with ES2015+ support
  • Bundle size: Minified and tree-shakeable
  • Module formats: ESM and UMD available

Node.js Support

  • Node.js 14.14+ (all maintained versions)
  • CommonJS and ESM module support
  • TypeScript definitions included

🌟 Why Developers Love doublell

  • Familiar yet powerful - All the convenience of arrays with O(1) insertion/deletion performance
  • TypeScript excellence - Comprehensive generic typing that makes development a breeze
  • Iterator protocol support - Use it anywhere you'd use an array with for...of, spread operator, and more
  • AI assistant friendly - Rich type information and documentation that coding assistants understand perfectly
  • Zero learning curve - If you know JavaScript arrays, you already know most of the API
  • Production ready - High test coverage, works in browsers and Node.js, battle-tested performance

📖 API Documentation

Complete API documentation with examples is available at https://typescript-oss.github.io/doublell/

🤝 Contributing

We welcome contributions! Please see our Contributing Guide for details.

📄 License

MIT © TypeScript OSS


Keywords: doubly-linked-list, data-structure, typescript, javascript, algorithm, performance, type-safe, iterator, functional-programming, generic, zero-dependencies, browser, nodejs, universal