@akashbabu/node-dll
Version:
DLL(doubly linked list) library for javascript projects
261 lines (254 loc) • 7.36 kB
JavaScript
(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 });
})));