UNPKG

@traxjs/trax

Version:

Reactive state management

152 lines (139 loc) 3.85 kB
interface ListItem<T> { value: T; next?: ListItem<T>; } /** * Pool of disposed items that can be reused */ let itemPool: ListItem<any> | undefined; /** * Add a list item to the item pool * @param itm */ function addToPool(itm: ListItem<any>) { itm.value = null as any; itm.next = itemPool; itemPool = itm; } /** * Get an item from the pool * @returns */ function getItemFromPool<T>(value: T, next?: ListItem<T>) { if (itemPool) { const itm = itemPool; itemPool = itm.next; itm.value = value; itm.next = next; return itm; } return undefined; } /** * Simple Linked list that can be used to manage stacks * Object properties are voluntarily kept minimal to minimize memory footprint */ export class LinkedList<T> { private _head?: ListItem<T>; private _size: number = 0; get head(): ListItem<T> | undefined { return this._head; } get size(): number { return this._size; } /** * Add a new value at the head of the list * @param value * @returns the list item */ add(value: T): ListItem<T> { const h = this._head; let itm: ListItem<T> = getItemFromPool(value, h) || { value, next: h }; this._head = itm; this._size++; return itm; } /** * Insert an item in the lhe linked list * @param fn function that decides where to insert the item. * Once the function returns an item, the iteration will stop */ insert(fn: (prev?: T, next?: T) => T | void) { let nd = this._head; let v: T | undefined; if (!nd) { v = fn() || undefined; if (v !== undefined) { this.add(v); } } else { let prev: ListItem<T> | undefined; while (prev || nd) { v = fn(prev?.value, nd?.value) || undefined; if (v !== undefined) { // insert the item let itm: ListItem<T> = getItemFromPool(v, nd) || { value: v, next: nd }; if (prev) { prev.next = itm; } else { this._head = itm; } this._size++; return; } prev = nd nd = nd?.next; } } } /** * Return the item value at the head of the list, * but doesn't remove it from the list * @returns the head value or undefined */ peek(): T | undefined { return this._head ? this._head.value : undefined; } /** * Return the item value at the head of the list, * and remove it from the list * @returns the head value or undefined */ shift(): T | undefined { const h = this._head; if (h) { const v = h.value; this._head = h.next; this._size--; addToPool(h); return v; } return undefined; } /** * Scan the list and remove the first item with the corresponding value * @param value * @returns true if an item was found and removed */ remove(value: T): boolean { let item = this._head, last: ListItem<T> | null = null; while (item) { if (item.value === value) { if (last) { // remove item last.next = item.next; this._size--; addToPool(item); } else { // value is the first item this.shift(); } return true; } last = item; item = item.next; } return false; } }