link-list-js
Version:
258 lines (233 loc) • 5.37 kB
JavaScript
class LinkNode {
/**
* @type {LinkNode}
*/
prev;
/**
* @type {LinkNode}
*/
next;
constructor(id, data, prev, next) {
this.id = id;
this.data = data;
this.prev = prev;
this.next = next;
}
}
class LinkList {
/**
* @type {LinkNode}
*/
#head;
/**
* @type {LinkNode}
*/
#tail;
/**
* @type {Map<any, LinkNode>}
*/
#nodes = new Map();
#idKey = "";
#parentKey = "";
constructor(data, idKey = "id", parentKey = "parent_id") {
this.#idKey = idKey;
this.#parentKey = parentKey;
if (Array.isArray(data)) {
data.forEach((item) => this.add(item));
}
}
/**
* Add data to LinkList
* @param {*} data
* @returns {boolean}
*/
add(data) {
if (!data) return false;
const id = data[this.#idKey];
const parentId = data[this.#parentKey];
const node = this.#nodes.get(id) || new LinkNode(id, data);
node.data = data;
if (parentId) {
let parent = this.#nodes.get(parentId);
if (!parent) {
parent = new LinkNode(parentId, undefined, undefined, node);
node.prev = parent;
this.#nodes.set(parentId, parent);
}
node.prev = parent;
parent.next = node;
}
this.#nodes.set(id, node);
if (!this.#head || !node.prev || this.#head.prev === node) {
this.#head = node;
}
if (!this.#tail || !node.next || this.#tail.next === node) {
this.#tail = node;
}
return true;
}
/**
* Delete item by Id
* @param {*} id
* @param {boolean} join auto remap linklist
*/
delete(id, update = true) {
const node = this.#nodes.get(id);
if (!node) return false;
if (node.prev) {
node.prev.next = node.next;
}
if (node.next) {
node.next.prev = node.prev;
}
if (update) {
if (node.prev && node.prev.data && node.next) {
node.prev.data[this.#idKey] = node.next.id;
}
if (node.next && node.next.data && node.prev) {
node.next.data[this.#parentKey] = node.prev.id;
}
}
this.#nodes.delete(id);
return true;
}
/**
* Update data in LinkList
* @param {*} data
*/
update(data) {
if (!data) return false;
const id = data[this.#idKey];
const node = this.#nodes.get(id);
if (!node) return false;
let needUpdateLink =
!node.data || data[this.#parentKey] !== node.data[this.#parentKey];
node.data = data;
if (needUpdateLink) {
const parentId = data[this.#parentKey];
if (parentId) {
const parent =
this.#nodes.get(parentId) ||
new LinkNode(parentId, undefined, undefined, node);
parent.next = node;
node.prev = parent;
this.#nodes.set(parentId, parent);
}
if (!node.next) this.#tail = node;
if (!node.prev) this.#head = node;
if (node === this.#head) {
let head = this.#head;
while (head && head.prev) {
head = head.prev;
}
this.#head = head;
}
if (node === this.#tail) {
let tail = this.#tail;
while (tail && tail.next) {
tail = tail.next;
}
this.#tail = tail;
}
}
return true;
}
/**
*
* @param {*} from
* @param {*} to
* @param {number} deep
*/
toArray(from = null, to = null, deep = 0) {
let i = 1;
let current = from ? this.#nodes.get(from) : this.#head;
const result = [];
while (current) {
if (current.data) result.push(current.data);
if (to && current === this.#nodes.get(to)) {
break;
}
if (deep && i === deep) {
break;
}
current = current.next;
i++;
}
return result;
}
setHead(id) {
const node = this.#nodes.get(id);
if (node) {
this.#head = node;
return true;
}
return false;
}
setTail(id) {
const node = this.#nodes.get(id);
if (node) {
this.#tail = node;
return true;
}
return false;
}
/**
* Return item by id
* @param {*} id
*/
getItemById(id) {
const node = this.#nodes.get(id);
return node && node.data;
}
/**
* Return parent of item
* @param {*} id
*/
getParent(id) {
const node = this.#nodes.get(id);
if (node && node.prev) return node.prev.data;
}
/**
* Return child of item
* @param {*} id
*/
getChild(id) {
const node = this.#nodes.get(id);
if (node && node.next) return node.next.data;
}
/**
* Add Item to end of linklist
* @param {*} data
*/
prepend(data) {
data[this.#parentKey] = null;
const id = data[this.#idKey];
const node = new LinkNode(id, data, null, this.#head);
if (this.#head) {
this.#head.prev = node;
if (this.#head.data) {
this.#head.data[this.#parentKey] = id;
}
}
this.#head = node;
this.#nodes.set(id, node);
}
/**
* Add Item to first of linklist
* @param {*} data
*/
append(data) {
if (!data) return;
data[this.#parentKey] = null;
const id = data[this.#idKey];
data[this.#parentKey] = this.#tail ? this.#tail.id : null;
const node = new LinkNode(id, data, this.#tail, null);
if (this.#tail) {
this.#tail.next = node;
this.#tail = node;
}
this.#tail = node;
this.#nodes.set(id, node);
}
}
module.exports = LinkList;