// eslint-disable-next-line @typescript-eslint/ban-types type TNonePrimitive = object export interface IDoublyLinkedItem { next: IDoublyLinkedItem prev: IDoublyLinkedItem thisObj: T readonly index: number list: DoublyLinkedList } export class DoublyLinkedList{ public first: IDoublyLinkedItem public last: IDoublyLinkedItem private _map = new WeakMap>() private _size: number = 0 insertAfter(element: T, predicate: (cmpObj: IDoublyLinkedItem) => boolean){ if (this._map.get(element)) { console.warn(`element has already been added to the doublylinkedlist`) return } const insertAfter = FindInLinkedList( this, predicate ) /** * if predicate can be found, then insert after the found entry * if not, then the previous first entry will be the next element */ const newDoublyLinkedItemNext = insertAfter ? insertAfter.next : this.first const newDoublyLinkedItem: IDoublyLinkedItem = { prev: insertAfter, next: newDoublyLinkedItemNext, thisObj: element, get index() { let count = 0, prev: IDoublyLinkedItem prev = this.prev while (prev) { prev = prev.prev count ++ } return count }, list: this } /** * set next of prev item * if prev is null, set first as this doublyitem */ if (insertAfter) insertAfter.next = newDoublyLinkedItem else this.first = newDoublyLinkedItem /** * set prev of next item * if next is null, set last as this doublyitem */ if (newDoublyLinkedItemNext) newDoublyLinkedItemNext.prev = newDoublyLinkedItem else this.last = newDoublyLinkedItem this._map.set(element, newDoublyLinkedItem) this._size ++ } remove(element: T) { const doublyLinkedItem = this._map.get(element) if (!doublyLinkedItem) { console.error(`doubly linked item not found`) return } const { next, prev } = doublyLinkedItem if (prev) prev.next = next if (next) next.prev = prev if (doublyLinkedItem === this.first) this.first = this.first.next if (doublyLinkedItem === this.last) this.last = this.last.prev // weakmap does not need to explicitly remove key/val // decrement the size this._size -- } size(){ return this._size } get(idx: number) { if (idx >= this.size()) throw new Error(`Index out of bound!`) let i = 0 let curr = this.first while (i < idx) { curr = curr.next if (!curr) throw new Error(`Index out of bound!`) i ++ } return curr } } export function FindInLinkedList(list: DoublyLinkedList, predicate: (element: IDoublyLinkedItem) => boolean){ let compareObj = list.first, returnObj: IDoublyLinkedItem = null if (!compareObj) return null do { if (predicate(compareObj)) { returnObj = compareObj break } compareObj = compareObj.next } while(!!compareObj) return returnObj }