UNPKG

@akashbabu/node-dll

Version:

DLL(doubly linked list) library for javascript projects

261 lines (254 loc) 7.36 kB
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define(['exports'], factory) : (global = global || self, factory(global.DLL = {})); }(this, (function (exports) { 'use strict'; class DLLItem { constructor(data, prev = null, next = null) { this.data = data; this.prev = prev; this.next = next; } } class DLLItemAccessRestrictor { /** * Grants all the access on the given dllItem * * @param accessRestrictedDllItem dll item whose access is restricted * * @returns all access granted dll item */ grantAccess(accessRestrictedDllItem) { return accessRestrictedDllItem.__dllItem__; } /** * Revokes write access to `prev` & `next` properties * * @param dllItem a node in the dll chain * * @returns Access restricted dll item */ revokeAccess(dllItem) { return (dllItem ? new AccessRestrictedDLLItem(dllItem) : null); } } class AccessRestrictedDLLItem { constructor(dllItem) { this.dllItem = dllItem; this.dllItemAccessRestrictor = new DLLItemAccessRestrictor(); this.__dllItem__ = dllItem; } get data() { return this.dllItem.data; } set data(dt) { this.dllItem.data = dt; } get prev() { return this.dllItemAccessRestrictor.revokeAccess(this.dllItem.prev); } get next() { return this.dllItemAccessRestrictor.revokeAccess(this.dllItem.next); } } class DLL { constructor() { this.state = this.getFreshState(); this.dllItemAccessRestrictor = new DLLItemAccessRestrictor(); } get head() { return this.dllItemAccessRestrictor.revokeAccess(this.state.head); } get tail() { return this.dllItemAccessRestrictor.revokeAccess(this.state.tail); } get length() { return this.state.length; } /** * Removes and returns the first * item in the list * * @returns Same data that was * used to append to this list */ shift() { let dllItem = this.state.head; if (!(dllItem instanceof DLLItem)) return undefined; this.remove(dllItem); const value = dllItem.data; // for gc dllItem = null; return value; } /** * Add the given item to the head of * DLL chain * * In other words the new item would * be the new head of the chain * * @returns Same data that was * used to append to this list */ unshift(data) { const currHead = this.state.head; const dllItem = new DLLItem(data, null, currHead); this.state.head = dllItem; if (currHead instanceof DLLItem) { currHead.prev = dllItem; // if HEAD is not set // then its an empty list } else { this.state.tail = dllItem; } this.state.length++; } /** * Iterate through the entire DLL chain * just like Array.forEach() * * @param cb iterator callback */ forEach(cb) { this.iterate((dllItem, i) => { cb(dllItem.data, i); }); } /** * Iterates through the entire DLL chain * and returns the result array, just * like Array.map() * * @param cb iterator callback * * @returns the result array just like Array.map() */ map(cb) { const mapped = []; this.forEach((value, i) => { mapped.push(cb(value, i)); }); return mapped; } /** * Adds the given item the tail of DLL * * @param data Data to be appended to the list * * @returns {DLLItem} dllItem, the same * can be used to remove this item from * DLL */ push(data) { return this.appendAfter(this.state.tail, data); } /** * Appends the given value after the * specified node * * @param node Node to append the new item * @param data Value for the new node * * @returns the newly appended node */ appendAfter(accessRestrictedNode, data) { let node; if (accessRestrictedNode === null && this.state.length > 0) { throw new Error('Invalid Node `null`: DLL is not empty, hence can\'t append to the given node'); } else if (accessRestrictedNode instanceof AccessRestrictedDLLItem) { node = this.dllItemAccessRestrictor.grantAccess(accessRestrictedNode); } else { node = accessRestrictedNode; } const dllItem = new DLLItem(data); // if node is null, then it means // that the list is empty if (node === null) { this.state.head = this.state.tail = dllItem; } else { dllItem.prev = node; dllItem.next = node.next; node.next = dllItem; // if the node was a tail node // then reset the tail node if (node === this.state.tail) { this.state.tail = dllItem; } } this.state.length++; return this.dllItemAccessRestrictor.revokeAccess(dllItem); } /** * Removes the given item from DLL * * @param dllItem */ remove(accessRestrictedDllItem) { let dllItem; if (accessRestrictedDllItem instanceof AccessRestrictedDLLItem) { dllItem = this.dllItemAccessRestrictor.grantAccess(accessRestrictedDllItem); } else if (accessRestrictedDllItem instanceof DLLItem) { dllItem = accessRestrictedDllItem; } else { return false; } // Isolate the node from the chain if (dllItem.prev) { dllItem.prev.next = dllItem.next; } else { // If it's HEAD node this.state.head = dllItem.next; } if (dllItem.next) { dllItem.next.prev = dllItem.prev; } else { // if it's also a TAIL node this.state.tail = dllItem.prev; } this.state.length--; return true; } /** * Clears the DLL chain */ clear() { // unlink all the items in the DLL chain // such it will be garbage collected // as no reference between them is found this.iterate((dllItem) => { dllItem.prev = dllItem.next = null; }); this.state = this.getFreshState(); } getFreshState() { return { length: 0, head: null, tail: null, }; } iterate(cb) { let dllItem = this.state.head; let i = 0; while (dllItem) { cb(dllItem, i++); dllItem = dllItem.next; } } } exports.DLL = DLL; exports.DLLItem = AccessRestrictedDLLItem; Object.defineProperty(exports, '__esModule', { value: true }); })));