Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit dd26a6b

Browse files
authoredNov 28, 2023
feat: add DoublyLInkedList (#21)
1 parent 9309eac commit dd26a6b

17 files changed

+1154
-307
lines changed
 

‎.husky/pre-commit

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#!/usr/bin/env sh
2+
. "$(dirname -- "$0")/_/husky.sh"
3+
4+
bun prettify
5+
bun lint

‎.husky/pre-push

-1
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,3 @@
22
. "$(dirname -- "$0")/_/husky.sh"
33

44
bun test
5-
bun lint

‎.vscode/settings.json

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"cSpell.words": ["isequal"]
3+
}

‎bun.lockb

2.83 KB
Binary file not shown.

‎commitlint.config.cjs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
module.exports = {
1+
module.exports = {
22
extends: ['@commitlint/config-conventional'],
3-
};
3+
};

‎lint-staged.config.cjs

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module.exports = {
2+
'**/*': ['eslint . --fix --ext .ts', 'prettier --write --ignore-unknown'],
3+
};

‎package.json

+11-3
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,39 @@
11
{
22
"name": "js-data-structures-implementation",
3-
"module": "src/index.ts",
3+
"type": "module",
4+
"exports": "./src/index.ts",
45
"scripts": {
56
"start": "bun run src/index.ts",
67
"commit": "cz",
78
"lint": "eslint . --fix --ext .ts",
8-
"test": "bun vitest",
9-
"test:watch": "bun vitest --watch",
9+
"test": "bun vitest run",
10+
"test:watch": "bun vitest",
11+
"prettify": "npx prettier . --write --ignore-unknown",
1012
"prepare": "husky install"
1113
},
1214
"devDependencies": {
1315
"@commitlint/cli": "^18.4.3",
1416
"@commitlint/config-conventional": "^18.4.3",
17+
"@types/lodash.isequal": "^4.5.8",
1518
"@typescript-eslint/eslint-plugin": "^6.12.0",
1619
"@typescript-eslint/parser": "^6.12.0",
1720
"bun-types": "^1.0.14",
1821
"commitizen": "^4.3.0",
22+
"cz-conventional-changelog": "^3.3.0",
1923
"eslint": "^8.54.0",
2024
"eslint-config-airbnb-typescript": "^17.1.0",
2125
"eslint-config-prettier": "^9.0.0",
2226
"eslint-plugin-jest": "^27.6.0",
2327
"eslint-plugin-prettier": "^5.0.1",
2428
"git-cz": "^4.9.0",
2529
"husky": "^8.0.3",
30+
"prettier": "^3.1.0",
2631
"vitest": "^0.34.6"
2732
},
2833
"peerDependencies": {
2934
"typescript": "^5.0.0"
35+
},
36+
"dependencies": {
37+
"lodash.isequal": "^4.5.0"
3038
}
3139
}

‎src/doubly-linked-list/doubly-linked-list-node.spec.ts

+19-17
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@ import { DoublyLinkedListNode } from './doubly-linked-list-node';
33

44
describe('DoublyLinkedListNode', () => {
55
it('creates list node with value', () => {
6-
// Arrange
7-
const list = new DoublyLinkedListNode<number>(1);
6+
// Act
7+
const doublyList = new DoublyLinkedListNode<number>(1);
88

9-
// Act with Assert
10-
expect(list.value).toBe(1);
11-
expect(list.next).toBeNull();
12-
expect(list.prev).toBeNull();
9+
// Assert
10+
expect(doublyList.value).toBe(1);
11+
expect(doublyList.next).toBeNull();
12+
expect(doublyList.prev).toBeNull();
1313
});
1414

1515
it('creates list node with object with value', () => {
@@ -19,8 +19,10 @@ describe('DoublyLinkedListNode', () => {
1919
key: 'test',
2020
};
2121

22+
// Act
2223
const list = new DoublyLinkedListNode<typeof expectedValue>(expectedValue);
2324

25+
// Assert
2426
expect(list.value).toEqual(expectedValue);
2527
expect(list.next).toBeNull();
2628
expect(list.prev).toBeNull();
@@ -29,10 +31,14 @@ describe('DoublyLinkedListNode', () => {
2931
it('links node together', () => {
3032
// Arrange
3133
const node2 = new DoublyLinkedListNode<number>(2);
34+
35+
// Act
3236
const node1 = new DoublyLinkedListNode<number>(1, node2);
37+
38+
// Act
3339
const node3 = new DoublyLinkedListNode<number>(3, node1, node2);
3440

35-
// Act and Assert
41+
// Assert
3642
expect(node1.next).toBeDefined();
3743
expect(node1.prev).toBeNull();
3844
expect(node2.next).toBeNull();
@@ -49,14 +55,8 @@ describe('DoublyLinkedListNode', () => {
4955
// Arrange
5056
const list = new DoublyLinkedListNode<number>(1);
5157

52-
// Assert
58+
// Act and Assert
5359
expect(list.toString()).toBe('1');
54-
55-
// Act
56-
list.value = 2;
57-
58-
// Assert
59-
expect(list.toString()).toBe('2');
6060
});
6161

6262
it('converts node to string with custom stringifier', () => {
@@ -67,11 +67,13 @@ describe('DoublyLinkedListNode', () => {
6767
};
6868

6969
const list = new DoublyLinkedListNode<typeof nodeValue>(nodeValue);
70-
7170
const toStringCallback = (x: typeof nodeValue) =>
7271
`value: ${x.value}, key: ${x.key}`;
7372

74-
// Act and Assert
75-
expect(list.toString(toStringCallback)).toBe('value: 1, key: test');
73+
// Act
74+
const received = list.toString(toStringCallback);
75+
76+
// Assert
77+
expect(received).toBe('value: 1, key: test');
7678
});
7779
});

‎src/doubly-linked-list/doubly-linked-list.spec.ts

+545-16
Large diffs are not rendered by default.

‎src/doubly-linked-list/doubly-linked-list.ts

+240-3
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,32 @@
11
import { DoublyLinkedListNode } from './doubly-linked-list-node';
2+
import { Comparator } from '../utils/comparator';
3+
import type { IComparator, CompareFunction } from '../utils/comparator';
24

3-
type NullableDoublyLinkedListNode<T> = DoublyLinkedListNode<T> | null;
5+
type NullableDoublyLinkedListNode<T = any> = DoublyLinkedListNode<T> | null;
6+
7+
type FindMethodOptions<T = any> = {
8+
value?: T;
9+
predicate?: (value: T) => boolean;
10+
};
411

512
export interface IDoublyLinkedList<T = any> {
613
readonly head: NullableDoublyLinkedListNode<T>;
714
readonly tail: NullableDoublyLinkedListNode<T>;
815
readonly length: number;
16+
readonly isEmpty: boolean;
17+
918
append(value: T): this;
19+
fromArray(array: T[]): this;
1020
toArray(): T[];
1121
toString(): string;
22+
prepend(value: T): this;
23+
delete(value: T): NullableDoublyLinkedListNode<T>;
24+
reverse(): this;
25+
insertAt(index: number, value: T): this;
26+
deleteHead(): NullableDoublyLinkedListNode<T>;
27+
deleteTail(): NullableDoublyLinkedListNode<T>;
28+
indexOf(value: T): number;
29+
find(options: FindMethodOptions<T>): NullableDoublyLinkedListNode<T>;
1230
}
1331

1432
export class DoublyLinkedList<T = any> implements IDoublyLinkedList<T> {
@@ -18,10 +36,13 @@ export class DoublyLinkedList<T = any> implements IDoublyLinkedList<T> {
1836

1937
#length: number;
2038

21-
constructor() {
39+
#compare: IComparator<T>;
40+
41+
constructor(compareFunction?: CompareFunction<T>) {
2242
this.#head = null;
2343
this.#tail = null;
2444
this.#length = 0;
45+
this.#compare = new Comparator(compareFunction);
2546
}
2647

2748
get head() {
@@ -36,15 +57,19 @@ export class DoublyLinkedList<T = any> implements IDoublyLinkedList<T> {
3657
return this.#length;
3758
}
3859

60+
get isEmpty() {
61+
return this.#head === null;
62+
}
63+
3964
append(value: T) {
4065
const newNode = new DoublyLinkedListNode(value);
4166

4267
if (this.#head === null) {
4368
this.#head = newNode;
4469
this.#tail = newNode;
4570
} else {
46-
this.#tail!.next = newNode;
4771
newNode.prev = this.#tail;
72+
this.#tail!.next = newNode;
4873
this.#tail = newNode;
4974
}
5075

@@ -53,6 +78,14 @@ export class DoublyLinkedList<T = any> implements IDoublyLinkedList<T> {
5378
return this;
5479
}
5580

81+
fromArray(array: T[]) {
82+
array.forEach((value) => {
83+
this.append(value);
84+
});
85+
86+
return this;
87+
}
88+
5689
toArray() {
5790
let array = [];
5891
let currentNode = this.#head;
@@ -68,4 +101,208 @@ export class DoublyLinkedList<T = any> implements IDoublyLinkedList<T> {
68101
toString() {
69102
return this.toArray().toString();
70103
}
104+
105+
prepend(value: T) {
106+
const newNode = new DoublyLinkedListNode(value);
107+
108+
if (this.#head === null) {
109+
this.#head = newNode;
110+
this.#tail = newNode;
111+
} else {
112+
newNode.next = this.#head;
113+
this.#head.prev = newNode;
114+
this.#head = newNode;
115+
}
116+
117+
this.#length += 1;
118+
119+
return this;
120+
}
121+
122+
delete(value: T) {
123+
if (this.#head === null) return null;
124+
125+
let deletedNode = null as NullableDoublyLinkedListNode;
126+
127+
// Delete from the beginning of the list.
128+
if (this.#compare.equal(value, this.#head.value)) {
129+
deletedNode = this.#head;
130+
this.#head = deletedNode.next;
131+
132+
// Update tail if the list becomes empty.
133+
if (this.#head === null) {
134+
this.#tail = null;
135+
} else {
136+
this.#head.prev = null;
137+
}
138+
} else {
139+
let currentNode = this.#head;
140+
141+
// Search for the node by value.
142+
while (
143+
currentNode.next &&
144+
!this.#compare.equal(value, currentNode.next.value)
145+
) {
146+
currentNode = currentNode.next;
147+
}
148+
149+
// Delete the node from the middle.
150+
if (currentNode.next !== null) {
151+
deletedNode = currentNode.next;
152+
currentNode.next = deletedNode.next;
153+
154+
if (currentNode.next === null) {
155+
this.#tail = currentNode;
156+
} else {
157+
currentNode.next.prev = currentNode;
158+
}
159+
}
160+
}
161+
162+
if (deletedNode) {
163+
this.#length -= 1;
164+
}
165+
166+
return deletedNode;
167+
}
168+
169+
reverse() {
170+
if (this.#head === null || this.#head.next === null) {
171+
return this;
172+
}
173+
174+
let prevNode = null;
175+
let currentNode = this.#head as NullableDoublyLinkedListNode;
176+
177+
while (currentNode !== null) {
178+
const nextNode = currentNode.next;
179+
currentNode.next = prevNode;
180+
currentNode.prev = nextNode;
181+
prevNode = currentNode;
182+
183+
currentNode = nextNode;
184+
}
185+
186+
this.#tail = this.#head;
187+
this.#head = prevNode;
188+
189+
return this;
190+
}
191+
192+
#findNodeByIndex(index: number) {
193+
let currentNode = this.#head!;
194+
195+
for (let i = 0; i < index; i += 1) {
196+
currentNode = currentNode.next!;
197+
}
198+
199+
return currentNode;
200+
}
201+
202+
insertAt(index: number, value: T) {
203+
const isInvalidIndex = index < 0 || index > this.#length;
204+
205+
if (isInvalidIndex) {
206+
throw new Error(
207+
'Index should be greater than or equal to 0 and less than or equal to the list length.',
208+
);
209+
}
210+
211+
if (index === 0) {
212+
// Insert at the beginning.
213+
this.prepend(value);
214+
// Insert at the end.
215+
} else if (index === this.#length) {
216+
this.append(value);
217+
} else {
218+
// Insert in the middle.
219+
let prevNode = this.#findNodeByIndex(index - 1);
220+
let newNode = new DoublyLinkedListNode(value);
221+
newNode.next = prevNode.next;
222+
newNode.prev = prevNode;
223+
prevNode.next = newNode;
224+
225+
this.#length += 1;
226+
}
227+
228+
return this;
229+
}
230+
231+
deleteHead() {
232+
if (this.#head === null) return null;
233+
234+
const deletedNode = this.#head;
235+
236+
if (deletedNode?.next) {
237+
this.#head = deletedNode.next;
238+
this.#head.prev = null;
239+
} else {
240+
this.#head = null;
241+
this.#tail = null;
242+
}
243+
244+
this.#length -= 1;
245+
246+
return deletedNode;
247+
}
248+
249+
deleteTail() {
250+
if (this.#head === null) return null;
251+
252+
let deletedNode = this.#tail;
253+
254+
// If there is only one node.
255+
if (this.#head === this.#tail) {
256+
this.#head = null;
257+
this.#tail = null;
258+
} else {
259+
// If multiple nodes.
260+
let currentNode = this.#head;
261+
262+
while (currentNode.next?.next) {
263+
currentNode = currentNode.next;
264+
}
265+
266+
currentNode.next = null;
267+
this.#tail = currentNode;
268+
}
269+
270+
this.#length -= 1;
271+
272+
return deletedNode;
273+
}
274+
275+
indexOf(value: T) {
276+
let count = 0;
277+
let currentNode = this.#head;
278+
279+
while (currentNode !== null) {
280+
if (this.#compare.equal(value, currentNode.value)) return count;
281+
282+
currentNode = currentNode.next;
283+
count += 1;
284+
}
285+
286+
return -1;
287+
}
288+
289+
find({ value, predicate }: FindMethodOptions<T>) {
290+
if (this.#head === null) return null;
291+
292+
let currentNode = this.#head as NullableDoublyLinkedListNode;
293+
294+
while (currentNode) {
295+
if (predicate && predicate(currentNode.value)) {
296+
return currentNode;
297+
}
298+
299+
if (value && this.#compare.equal(value, currentNode.value)) {
300+
return currentNode;
301+
}
302+
303+
currentNode = currentNode.next;
304+
}
305+
306+
return null;
307+
}
71308
}

‎src/linked-list/lined-list-node.spec.ts

+10-8
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,10 @@ describe('LinkedListNode', () => {
2828
});
2929

3030
it('links nodes together', () => {
31-
// Arrange and Act
31+
// Arrange
3232
const node2 = new LinkedListNode<number>(1);
33+
34+
// Act
3335
const node1 = new LinkedListNode<number>(2, node2);
3436

3537
// Assert
@@ -43,13 +45,9 @@ describe('LinkedListNode', () => {
4345
it('converts node to string', () => {
4446
// Arrange
4547
const node = new LinkedListNode<number>(1);
46-
expect(node.toString()).toEqual('1');
47-
48-
// Act
49-
node.value = 2;
5048

51-
// Assert
52-
expect(node.toString()).toEqual('2');
49+
// Act and Assert
50+
expect(node.toString()).toEqual('1');
5351
});
5452

5553
it('converts node to string with custom stringifier', () => {
@@ -63,7 +61,11 @@ describe('LinkedListNode', () => {
6361
const toStringCallback = (x: typeof nodeValue) =>
6462
`value: ${x.value}, key: ${x.key}`;
6563

66-
expect(list.toString(toStringCallback)).toBe('value: 1, key: test');
64+
// Act
65+
const received = list.toString(toStringCallback);
66+
67+
// Assert
68+
expect(received).toBe('value: 1, key: test');
6769
});
6870
});
6971
});

‎src/linked-list/linked-list.spec.ts

+228-196
Large diffs are not rendered by default.

‎src/linked-list/linked-list.ts

+71-52
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import { LinkedListNode } from './linked-list-node';
2+
import { Comparator } from '../utils/comparator';
3+
import type { IComparator, CompareFunction } from '../utils/comparator';
24

35
type NullableLinkedListNode<T = any> = LinkedListNode<T> | null;
46
type FindMethodOptions<T = any> = {
@@ -12,29 +14,34 @@ export interface ILinkedList<T = any> {
1214
readonly length: number;
1315
readonly isEmpty: boolean;
1416

17+
append(value: T): this;
18+
fromArray(array: T[]): this;
1519
toArray(): T[];
1620
toString(): string;
17-
append(value: T): this;
1821
prepend(value: T): this;
19-
reverse(): this;
2022
delete(value: T): NullableLinkedListNode<T>;
23+
reverse(): this;
2124
insertAt(index: number, value: T): this;
2225
deleteHead(): NullableLinkedListNode<T>;
2326
deleteTail(): NullableLinkedListNode<T>;
2427
indexOf(value: T): number;
25-
fromArray(array: T[]): this;
2628
find(options: FindMethodOptions<T>): NullableLinkedListNode<T>;
2729
}
2830

2931
export class LinkedList<T = any> implements ILinkedList<T> {
30-
#head: NullableLinkedListNode<T> = null;
32+
#head: NullableLinkedListNode<T>;
3133

32-
#tail: NullableLinkedListNode<T> = null;
34+
#tail: NullableLinkedListNode<T>;
3335

34-
#length: number = 0;
36+
#length: number;
3537

36-
get isEmpty() {
37-
return this.#head === null;
38+
#compare: IComparator<T>;
39+
40+
constructor(compareFunction?: CompareFunction<T>) {
41+
this.#head = null;
42+
this.#tail = null;
43+
this.#length = 0;
44+
this.#compare = new Comparator(compareFunction);
3845
}
3946

4047
get head() {
@@ -45,24 +52,12 @@ export class LinkedList<T = any> implements ILinkedList<T> {
4552
return this.#tail;
4653
}
4754

48-
get length(): number {
55+
get length() {
4956
return this.#length;
5057
}
5158

52-
toArray() {
53-
const array = [];
54-
let currentNode = this.#head;
55-
56-
while (currentNode) {
57-
array.push(currentNode.value);
58-
currentNode = currentNode.next;
59-
}
60-
61-
return array;
62-
}
63-
64-
toString() {
65-
return this.toArray().toString();
59+
get isEmpty() {
60+
return this.#head === null;
6661
}
6762

6863
append(value: T) {
@@ -81,6 +76,30 @@ export class LinkedList<T = any> implements ILinkedList<T> {
8176
return this;
8277
}
8378

79+
fromArray(array: T[]) {
80+
array.forEach((value) => {
81+
this.append(value);
82+
});
83+
84+
return this;
85+
}
86+
87+
toArray() {
88+
const array = [];
89+
let currentNode = this.#head;
90+
91+
while (currentNode) {
92+
array.push(currentNode.value);
93+
currentNode = currentNode.next;
94+
}
95+
96+
return array;
97+
}
98+
99+
toString() {
100+
return this.toArray().toString();
101+
}
102+
84103
prepend(value: T) {
85104
const newNode = new LinkedListNode(value);
86105

@@ -102,29 +121,32 @@ export class LinkedList<T = any> implements ILinkedList<T> {
102121

103122
let deletedNode = null as NullableLinkedListNode;
104123

105-
// Delete from the beginning of the list
106-
if (value === this.#head.value) {
124+
// Delete from the beginning of the list.
125+
if (this.#compare.equal(value, this.#head.value)) {
107126
deletedNode = this.#head;
108127
this.#head = deletedNode.next;
109128

110-
// Update tail if the list becomes empty
129+
// Update tail if the list becomes empty.
111130
if (this.#head === null) {
112131
this.#tail = null;
113132
}
114133
} else {
115134
let currentNode = this.#head;
116135

117-
// Search for the node by value
118-
while (currentNode.next && value !== currentNode.next.value) {
136+
// Search for the node by value.
137+
while (
138+
currentNode.next &&
139+
!this.#compare.equal(value, currentNode.next.value)
140+
) {
119141
currentNode = currentNode.next;
120142
}
121143

122-
// Delete the node from the middle
144+
// Delete the node from the middle.
123145
if (currentNode.next !== null) {
124146
deletedNode = currentNode.next;
125147
currentNode.next = deletedNode?.next;
126148

127-
// Update tail if the last node is deleted
149+
// Update tail if the last node is deleted.
128150
if (currentNode.next === null) {
129151
this.#tail = currentNode;
130152
}
@@ -139,14 +161,17 @@ export class LinkedList<T = any> implements ILinkedList<T> {
139161
}
140162

141163
reverse() {
142-
if (this.#head === null || this.#head.next === null) return this;
164+
if (this.#head === null || this.#head.next === null) {
165+
return this;
166+
}
143167

144168
let currentNode = this.#head as NullableLinkedListNode;
145169
let prevNode = null;
146170

147-
while (currentNode) {
171+
while (currentNode !== null) {
148172
const nextNode = currentNode.next;
149-
[currentNode.next, prevNode] = [prevNode, currentNode];
173+
currentNode.next = prevNode;
174+
prevNode = currentNode;
150175

151176
currentNode = nextNode;
152177
}
@@ -168,20 +193,22 @@ export class LinkedList<T = any> implements ILinkedList<T> {
168193
}
169194

170195
insertAt(index: number, value: T): this {
171-
if (index < 0 || index > this.#length) {
196+
const isInvalidIndex = index < 0 || index > this.#length;
197+
198+
if (isInvalidIndex) {
172199
throw new Error(
173200
'Index should be greater than or equal to 0 and less than or equal to the list length.',
174201
);
175202
}
176203

177204
if (index === 0) {
178-
// Insert at the beginning
205+
// Insert at the beginning.
179206
this.prepend(value);
180207
} else if (index === this.#length) {
181-
// Insert end
208+
// Insert at the end.
182209
this.append(value);
183210
} else {
184-
// Insert in the middle
211+
// Insert in the middle.
185212
const prevNode = this.#findNodeByIndex(index - 1);
186213
const newNode = new LinkedListNode(value);
187214

@@ -199,8 +226,8 @@ export class LinkedList<T = any> implements ILinkedList<T> {
199226

200227
const deletedNode = this.#head;
201228

202-
if (this.#head?.next) {
203-
this.#head = this.#head.next;
229+
if (deletedNode?.next) {
230+
this.#head = deletedNode.next;
204231
} else {
205232
this.#head = null;
206233
this.#tail = null;
@@ -239,10 +266,10 @@ export class LinkedList<T = any> implements ILinkedList<T> {
239266

240267
indexOf(value: T) {
241268
let count = 0;
242-
let currentNode = this.head;
269+
let currentNode = this.#head;
243270

244-
while (currentNode) {
245-
if (value === currentNode.value) return count;
271+
while (currentNode !== null) {
272+
if (this.#compare.equal(value, currentNode.value)) return count;
246273

247274
currentNode = currentNode.next;
248275
count += 1;
@@ -251,25 +278,17 @@ export class LinkedList<T = any> implements ILinkedList<T> {
251278
return -1;
252279
}
253280

254-
fromArray(array: T[]) {
255-
array.forEach((value) => {
256-
this.append(value);
257-
});
258-
259-
return this;
260-
}
261-
262281
find({ value, predicate }: FindMethodOptions<T>) {
263282
if (this.#head === null) return null;
264283

265-
let currentNode = this.head as NullableLinkedListNode;
284+
let currentNode = this.#head as NullableLinkedListNode;
266285

267286
while (currentNode) {
268287
if (predicate && predicate(currentNode.value)) {
269288
return currentNode;
270289
}
271290

272-
if (value !== undefined && value === currentNode.value) {
291+
if (value && this.#compare.equal(value, currentNode.value)) {
273292
return currentNode;
274293
}
275294

‎src/utils/comparator/comparator.spec.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ describe('Comparator', () => {
77
// Arrange
88
const comparator = new Comparator<number>();
99

10-
// Assert
10+
// Act and Assert
1111
expect(comparator.lessThan(2, 5)).toBeTruthy();
1212
expect(comparator.greaterThan(5, 2)).toBeTruthy();
1313
expect(comparator.equal(2, 2)).toBeTruthy();

‎src/utils/comparator/comparator.ts

+13-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
import isEqual from 'lodash.isequal';
2+
13
export type CompareFunction<T> = (a: T, b: T) => -1 | 0 | 1;
24

3-
export interface ComparatorType<T> {
5+
export interface IComparator<T> {
46
equal(a: T, b: T): boolean;
57
lessThan(a: T, b: T): boolean;
68
greaterThan(a: T, b: T): boolean;
@@ -9,21 +11,27 @@ export interface ComparatorType<T> {
911
reverse(): void;
1012
}
1113

12-
export class Comparator<T = any> implements ComparatorType<T> {
13-
#compare: (a: any, b: any) => -1 | 0 | 1;
14+
export class Comparator<T = any> implements IComparator<T> {
15+
#compare: CompareFunction<T>;
16+
17+
#isEqualFn: typeof isEqual;
1418

1519
static defaultCompareFunction<T>(a: T, b: T) {
1620
if (a === b) return 0;
1721

1822
return a < b ? -1 : 1;
1923
}
2024

21-
constructor(compareFunction?: CompareFunction<T>) {
25+
constructor(
26+
compareFunction?: CompareFunction<T>,
27+
isEqualFn: typeof isEqual = isEqual,
28+
) {
2229
this.#compare = compareFunction || Comparator.defaultCompareFunction;
30+
this.#isEqualFn = isEqualFn;
2331
}
2432

2533
equal(a: T, b: T) {
26-
return this.#compare(a, b) === 0;
34+
return this.#isEqualFn(a, b);
2735
}
2836

2937
lessThan(a: T, b: T) {

‎tsconfig.eslint.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
{
22
"extends": "./tsconfig.json",
3-
"include": ["src/**/*.ts"],
4-
}
3+
"include": ["src/**/*.ts"]
4+
}

‎tsconfig.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,5 @@
1919
"bun-types" // add Bun global
2020
]
2121
},
22-
"include": ["src/**/*"],
22+
"include": ["src/**/*"]
2323
}

0 commit comments

Comments
 (0)
Please sign in to comment.