123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201 |
- import type LinkedNode from './linked-node.js';
- class LinkedList<T extends LinkedNode> {
- public head: T | null;
- public tail: T | null;
- public length: number;
- constructor() {
- this.head = null;
- this.tail = null;
- this.length = 0;
- }
- public append(...nodes: T[]): void {
- this.insertBefore(nodes[0], null);
- if (nodes.length > 1) {
- const rest = nodes.slice(1);
- this.append(...rest);
- }
- }
- public at(index: number): T | null {
- const next = this.iterator();
- let cur = next();
- while (cur && index > 0) {
- index -= 1;
- cur = next();
- }
- return cur;
- }
- public contains(node: T): boolean {
- const next = this.iterator();
- let cur = next();
- while (cur) {
- if (cur === node) {
- return true;
- }
- cur = next();
- }
- return false;
- }
- public indexOf(node: T): number {
- const next = this.iterator();
- let cur = next();
- let index = 0;
- while (cur) {
- if (cur === node) {
- return index;
- }
- index += 1;
- cur = next();
- }
- return -1;
- }
- public insertBefore(node: T | null, refNode: T | null): void {
- if (node == null) {
- return;
- }
- this.remove(node);
- node.next = refNode;
- if (refNode != null) {
- node.prev = refNode.prev;
- if (refNode.prev != null) {
- refNode.prev.next = node;
- }
- refNode.prev = node;
- if (refNode === this.head) {
- this.head = node;
- }
- } else if (this.tail != null) {
- this.tail.next = node;
- node.prev = this.tail;
- this.tail = node;
- } else {
- node.prev = null;
- this.head = this.tail = node;
- }
- this.length += 1;
- }
- public offset(target: T): number {
- let index = 0;
- let cur = this.head;
- while (cur != null) {
- if (cur === target) {
- return index;
- }
- index += cur.length();
- cur = cur.next as T;
- }
- return -1;
- }
- public remove(node: T): void {
- if (!this.contains(node)) {
- return;
- }
- if (node.prev != null) {
- node.prev.next = node.next;
- }
- if (node.next != null) {
- node.next.prev = node.prev;
- }
- if (node === this.head) {
- this.head = node.next as T;
- }
- if (node === this.tail) {
- this.tail = node.prev as T;
- }
- this.length -= 1;
- }
- public iterator(curNode: T | null = this.head): () => T | null {
- // TODO use yield when we can
- return (): T | null => {
- const ret = curNode;
- if (curNode != null) {
- curNode = curNode.next as T;
- }
- return ret;
- };
- }
- public find(index: number, inclusive = false): [T | null, number] {
- const next = this.iterator();
- let cur = next();
- while (cur) {
- const length = cur.length();
- if (
- index < length ||
- (inclusive &&
- index === length &&
- (cur.next == null || cur.next.length() !== 0))
- ) {
- return [cur, index];
- }
- index -= length;
- cur = next();
- }
- return [null, 0];
- }
- public forEach(callback: (cur: T) => void): void {
- const next = this.iterator();
- let cur = next();
- while (cur) {
- callback(cur);
- cur = next();
- }
- }
- public forEachAt(
- index: number,
- length: number,
- callback: (cur: T, offset: number, length: number) => void,
- ): void {
- if (length <= 0) {
- return;
- }
- const [startNode, offset] = this.find(index);
- let curIndex = index - offset;
- const next = this.iterator(startNode);
- let cur = next();
- while (cur && curIndex < index + length) {
- const curLength = cur.length();
- if (index > curIndex) {
- callback(
- cur,
- index - curIndex,
- Math.min(length, curIndex + curLength - index),
- );
- } else {
- callback(cur, 0, Math.min(curLength, index + length - curIndex));
- }
- curIndex += curLength;
- cur = next();
- }
- }
- public map(callback: (cur: T) => any): any[] {
- return this.reduce((memo: T[], cur: T) => {
- memo.push(callback(cur));
- return memo;
- }, []);
- }
- public reduce<M>(callback: (memo: M, cur: T) => M, memo: M): M {
- const next = this.iterator();
- let cur = next();
- while (cur) {
- memo = callback(memo, cur);
- cur = next();
- }
- return memo;
- }
- }
- export default LinkedList;
|