Skip to content

Commit 91afa09

Browse files
committed
feat(linked-list): Add EmptyAwareSinglyLinkedList and enhance DoublyLinkedList functionality
- Introduced `EmptyAwareSinglyLinkedList`, a subclass of `SinglyLinkedList` that emits an `empty` event when the list becomes empty due to `reset`, `shift`, or `remove` operations. - Added `nodes()` iterator method to `DoublyLinkedList` for iterating over nodes directly. - Enhanced unit tests for `DoublyLinkedList` and `SinglyLinkedList` to cover edge cases and new functionality. - Added comprehensive tests for `EmptyAwareSinglyLinkedList` to validate `empty` event emission under various scenarios. - Improved code formatting and consistency.
1 parent 8019c61 commit 91afa09

File tree

2 files changed

+124
-28
lines changed

2 files changed

+124
-28
lines changed
Lines changed: 85 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,138 +1,197 @@
1-
import { SinglyLinkedList, DoublyLinkedList } from './linked-list';
2-
import { equal, deepEqual } from 'assert/strict';
3-
4-
describe('DoublyLinkedList', () => {
1+
import {
2+
SinglyLinkedList,
3+
DoublyLinkedList,
4+
EmptyAwareSinglyLinkedList,
5+
} from "./linked-list";
6+
import { equal, deepEqual } from "assert/strict";
7+
8+
describe("DoublyLinkedList", () => {
59
const list = new DoublyLinkedList();
610

7-
it('should start empty', () => {
11+
it("should start empty", () => {
812
equal(list.length, 0);
913
equal(list.head, undefined);
1014
equal(list.tail, undefined);
1115
deepEqual(Array.from(list), []);
1216
});
1317

14-
it('shift empty', () => {
18+
it("shift empty", () => {
1519
equal(list.shift(), undefined);
1620
equal(list.length, 0);
1721
deepEqual(Array.from(list), []);
1822
});
1923

20-
it('push 1', () => {
24+
it("push 1", () => {
2125
list.push(1);
2226
equal(list.length, 1);
2327
deepEqual(Array.from(list), [1]);
2428
});
2529

26-
it('push 2', () => {
30+
it("push 2", () => {
2731
list.push(2);
2832
equal(list.length, 2);
2933
deepEqual(Array.from(list), [1, 2]);
3034
});
3135

32-
it('unshift 0', () => {
36+
it("unshift 0", () => {
3337
list.unshift(0);
3438
equal(list.length, 3);
3539
deepEqual(Array.from(list), [0, 1, 2]);
3640
});
3741

38-
it('remove middle node', () => {
42+
it("remove middle node", () => {
3943
list.remove(list.head!.next!);
4044
equal(list.length, 2);
4145
deepEqual(Array.from(list), [0, 2]);
4246
});
4347

44-
it('remove head', () => {
48+
it("remove head", () => {
4549
list.remove(list.head!);
4650
equal(list.length, 1);
4751
deepEqual(Array.from(list), [2]);
4852
});
4953

50-
it('remove tail', () => {
54+
it("remove tail", () => {
5155
list.remove(list.tail!);
5256
equal(list.length, 0);
5357
deepEqual(Array.from(list), []);
5458
});
5559

56-
it('unshift empty queue', () => {
60+
it("unshift empty queue", () => {
5761
list.unshift(0);
5862
equal(list.length, 1);
5963
deepEqual(Array.from(list), [0]);
6064
});
6165

62-
it('push 1', () => {
66+
it("push 1", () => {
6367
list.push(1);
6468
equal(list.length, 2);
6569
deepEqual(Array.from(list), [0, 1]);
6670
});
6771

68-
it('shift', () => {
72+
it("shift", () => {
6973
equal(list.shift(), 0);
7074
equal(list.length, 1);
7175
deepEqual(Array.from(list), [1]);
7276
});
7377

74-
it('shift last element', () => {
78+
it("shift last element", () => {
7579
equal(list.shift(), 1);
7680
equal(list.length, 0);
7781
deepEqual(Array.from(list), []);
7882
});
83+
84+
it("provide forEach for nodes", () => {
85+
list.reset();
86+
list.push(1);
87+
list.push(2);
88+
list.push(3);
89+
let count = 0;
90+
for(const _ of list.nodes()) {
91+
count++;
92+
}
93+
equal(count, 3);
94+
for(const _ of list.nodes()) {
95+
count++;
96+
}
97+
equal(count, 6);
98+
});
7999
});
80100

81-
describe('SinglyLinkedList', () => {
101+
describe("SinglyLinkedList", () => {
82102
const list = new SinglyLinkedList();
83103

84-
it('should start empty', () => {
104+
it("should start empty", () => {
85105
equal(list.length, 0);
86106
equal(list.head, undefined);
87107
equal(list.tail, undefined);
88108
deepEqual(Array.from(list), []);
89109
});
90110

91-
it('shift empty', () => {
111+
it("shift empty", () => {
92112
equal(list.shift(), undefined);
93113
equal(list.length, 0);
94114
deepEqual(Array.from(list), []);
95115
});
96116

97-
it('push 1', () => {
117+
it("push 1", () => {
98118
list.push(1);
99119
equal(list.length, 1);
100120
deepEqual(Array.from(list), [1]);
101121
});
102122

103-
it('push 2', () => {
123+
it("push 2", () => {
104124
list.push(2);
105125
equal(list.length, 2);
106126
deepEqual(Array.from(list), [1, 2]);
107127
});
108128

109-
it('push 3', () => {
129+
it("push 3", () => {
110130
list.push(3);
111131
equal(list.length, 3);
112132
deepEqual(Array.from(list), [1, 2, 3]);
113133
});
114134

115-
it('shift 1', () => {
135+
it("shift 1", () => {
116136
equal(list.shift(), 1);
117137
equal(list.length, 2);
118138
deepEqual(Array.from(list), [2, 3]);
119139
});
120140

121-
it('shift 2', () => {
141+
it("shift 2", () => {
122142
equal(list.shift(), 2);
123143
equal(list.length, 1);
124144
deepEqual(Array.from(list), [3]);
125145
});
126146

127-
it('shift 3', () => {
147+
it("shift 3", () => {
128148
equal(list.shift(), 3);
129149
equal(list.length, 0);
130150
deepEqual(Array.from(list), []);
131151
});
132152

133-
it('should be empty', () => {
153+
it("should be empty", () => {
134154
equal(list.length, 0);
135155
equal(list.head, undefined);
136156
equal(list.tail, undefined);
137157
});
138158
});
159+
160+
describe("EmptyAwareSinglyLinkedList", () => {
161+
it("should emit 'empty' event when reset", () => {
162+
const list = new EmptyAwareSinglyLinkedList<number>();
163+
let count = 0;
164+
list.events.on("empty", () => count++);
165+
list.push(1);
166+
list.reset();
167+
equal(count, 1);
168+
list.reset();
169+
equal(count, 1);
170+
});
171+
172+
it("should emit 'empty' event when shift makes the list empty", () => {
173+
const list = new EmptyAwareSinglyLinkedList<number>();
174+
let count = 0;
175+
list.events.on("empty", () => count++);
176+
list.push(1);
177+
list.push(2);
178+
list.shift();
179+
equal(count, 0);
180+
list.shift();
181+
equal(count, 1);
182+
list.shift();
183+
equal(count, 1);
184+
});
185+
186+
it("should emit 'empty' event when remove makes the list empty", () => {
187+
const list = new EmptyAwareSinglyLinkedList<number>();
188+
let count = 0;
189+
list.events.on("empty", () => count++);
190+
const node1 = list.push(1);
191+
const node2 = list.push(2);
192+
list.remove(node1, undefined);
193+
equal(count, 0);
194+
list.remove(node2, undefined);
195+
equal(count, 1);
196+
});
197+
});

packages/client/lib/client/linked-list.ts

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import EventEmitter from "events";
2+
13
export interface DoublyLinkedNode<T> {
24
value: T;
35
previous: DoublyLinkedNode<T> | undefined;
@@ -32,7 +34,7 @@ export class DoublyLinkedList<T> {
3234
next: undefined,
3335
value
3436
};
35-
}
37+
}
3638

3739
return this.#tail = this.#tail.next = {
3840
previous: this.#tail,
@@ -93,7 +95,7 @@ export class DoublyLinkedList<T> {
9395
node.previous!.next = node.next;
9496
node.previous = undefined;
9597
}
96-
98+
9799
node.next = undefined;
98100
}
99101

@@ -109,6 +111,14 @@ export class DoublyLinkedList<T> {
109111
node = node.next;
110112
}
111113
}
114+
115+
*nodes() {
116+
let node = this.#head;
117+
while(node) {
118+
yield node;
119+
node = node.next;
120+
}
121+
}
112122
}
113123

114124
export interface SinglyLinkedNode<T> {
@@ -201,3 +211,30 @@ export class SinglyLinkedList<T> {
201211
}
202212
}
203213
}
214+
215+
export class EmptyAwareSinglyLinkedList<T> extends SinglyLinkedList<T> {
216+
readonly events = new EventEmitter();
217+
reset() {
218+
const old = this.length;
219+
super.reset();
220+
if(old !== this.length && this.length === 0) {
221+
this.events.emit('empty');
222+
}
223+
}
224+
shift(): T | undefined {
225+
const old = this.length;
226+
const ret = super.shift();
227+
if(old !== this.length && this.length === 0) {
228+
this.events.emit('empty');
229+
}
230+
return ret;
231+
}
232+
remove(node: SinglyLinkedNode<T>, parent: SinglyLinkedNode<T> | undefined) {
233+
const old = this.length;
234+
super.remove(node, parent);
235+
if(old !== this.length && this.length === 0) {
236+
this.events.emit('empty');
237+
}
238+
}
239+
240+
}

0 commit comments

Comments
 (0)