Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 353b2ae

Browse files
committed
Add unit tests for singly and doubly linked list. fix bug with pop where new tail node was not being assigned the proper node as its next prop
1 parent bde2d44 commit 353b2ae

13 files changed

+4981
-42
lines changed

‎dist/datastructures/DoublyLinkedList.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ class DoublyLinkedList extends SinglyLinkedList_1.default {
1616
this.pop = () => {
1717
// returns deleted item, O(1) time, unlike SinglyLinkedList's O(n) pop
1818
if (!this.tail)
19-
returnnull;
19+
return;
2020
const oldTail = this.tail;
2121
if (oldTail.prev) {
2222
this.tail = oldTail.prev;
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
"use strict";
2+
var __importDefault = (this && this.__importDefault) || function (mod) {
3+
return (mod && mod.__esModule) ? mod : { "default": mod };
4+
};
5+
Object.defineProperty(exports, "__esModule", { value: true });
6+
const DoublyLinkedList_1 = __importDefault(require("./DoublyLinkedList"));
7+
const SinglyLinkedList_test_1 = require("./SinglyLinkedList.test");
8+
function testTraversalDLL(list) {
9+
// backwards traversal
10+
if (!list.tail)
11+
return true; // empty list
12+
let node = list.tail;
13+
let countedLength = 1;
14+
while (node) {
15+
// console.log(node.data)
16+
if (node.prev) {
17+
node = node.prev;
18+
countedLength++;
19+
}
20+
else
21+
break;
22+
}
23+
return (node === list.head && countedLength === list.length);
24+
}
25+
exports.testTraversalDLL = testTraversalDLL;
26+
test('popping from an empty list returns void', () => {
27+
const list = new DoublyLinkedList_1.default();
28+
expect(list.pop()).toBeUndefined();
29+
});
30+
test('shifting from an empty list returns void', () => {
31+
const list = new DoublyLinkedList_1.default();
32+
expect(list.shift()).toBeUndefined();
33+
});
34+
test('pushed node to empty list, node -> new head & tail', () => {
35+
const list = new DoublyLinkedList_1.default();
36+
list.push('value');
37+
expect(list.head.data === 'value' && list.tail.data === 'value').toBe(true);
38+
});
39+
test('unshifting node to empty list, node -> new head & tail', () => {
40+
const list = new DoublyLinkedList_1.default();
41+
list.unshift('value');
42+
expect(list.head.data === 'value' && list.tail.data === 'value').toBe(true);
43+
});
44+
test('insert as 0, or without index, on empty list, node -> new head & tail', () => {
45+
const list = new DoublyLinkedList_1.default();
46+
list.insert('value', 0);
47+
expect(list.head.data === 'value' && list.tail.data === 'value').toBe(true);
48+
});
49+
test('Able to insert in the middle of the list and maintain traversal', () => {
50+
const list = new DoublyLinkedList_1.default();
51+
list.push('Van Gogh');
52+
list.unshift('Matisse');
53+
list.push('Gauguin');
54+
list.unshift('Picasso');
55+
list.insert('Toulouse-Lautrec', 1);
56+
expect(SinglyLinkedList_test_1.testTraverseSLL(list)).toBe(true);
57+
expect(testTraversalDLL(list)).toBe(true);
58+
});
59+
test(`Able to traverse a list mutated by pushes, pops, shifts, unshifts, inserts, and removals all the way from head to tail`, () => {
60+
const list = new DoublyLinkedList_1.default();
61+
for (let i = 1; i < 100; i++) {
62+
if (list.length > 1 && list.head.next === null) {
63+
console.log('caught length inconsistency at: ', i);
64+
console.log(list.log());
65+
throw new Error;
66+
}
67+
if (i % 2 === 0) {
68+
list.push(`push: ${i}`);
69+
}
70+
if (i % 3 === 0) {
71+
list.unshift(`unshift: ${i}`);
72+
}
73+
if (i % 5 === 0) {
74+
list.pop();
75+
}
76+
if (i % 7 === 0) {
77+
list.shift();
78+
}
79+
if (i % 9 === 0) {
80+
list.insert(`insert: ${i}`, Math.floor(Math.random() * list.length));
81+
}
82+
if (i % 11 === 0) {
83+
list.removeIndex(Math.floor(Math.random() * list.length));
84+
}
85+
}
86+
expect(SinglyLinkedList_test_1.testTraverseSLL(list)).toBe(true);
87+
expect(testTraversalDLL(list)).toBe(true);
88+
});

‎dist/datastructures/SinglyLinkedList.js

Lines changed: 57 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
44
};
55
Object.defineProperty(exports, "__esModule", { value: true });
66
const Node_1 = __importDefault(require("./Node"));
7+
const IndexError_1 = __importDefault(require("../utils/IndexError"));
78
class SinglyLinkedList {
89
constructor() {
10+
// TODO: use constructor to initiate a filled linked list like you can do with sets?
911
this._length = 0;
1012
this.head = null;
1113
this.tail = null;
@@ -14,7 +16,7 @@ class SinglyLinkedList {
1416
// ? maybe make length a public getter with a private setter so that it's readonly outside
1517
// ? the class, but modifiable inside by the other methods?
1618
// when implemented, tsc error: Getter and setter accessors do not agree in visibility.ts(2379)
17-
// typescript doesn't support this,
19+
// typescript doesn't support this,
1820
// see https://github.com/microsoft/TypeScript/issues/2845
1921
// design meeting notes: https://github.com/microsoft/TypeScript/issues/6735
2022
// instead, i'm using a slightly uglier work around where there is no setter,
@@ -42,42 +44,48 @@ class SinglyLinkedList {
4244
// O(n) time since we have to iterate over the whole list to find the new tail,
4345
// and make the new tail's next point to null.
4446
if (!this.head)
45-
returnnull;
46-
const node = this.head;
47+
return;
48+
let node = this.head;
4749
if (!node.next) {
4850
this.head = null;
4951
this.tail = null;
5052
this._length--;
5153
return node;
5254
}
5355
else {
54-
let childNode = node.next;
55-
while (childNode.next !== null) {
56+
console.assert(this.length > 1);
57+
let targetNode = node.next;
58+
while (targetNode.next !== null) {
5659
// list traversal
57-
childNode = childNode.next;
60+
node = targetNode;
61+
targetNode = targetNode.next;
5862
}
5963
node.next = null;
6064
this.tail = node;
6165
this._length--;
62-
return childNode;
66+
return targetNode;
6367
}
6468
}
6569
unshift(value) {
6670
// add node as first of list
6771
// O(1)
6872
const node = new Node_1.default(value);
69-
if (!this.length)
73+
if (!this.head) {
74+
this.head = node;
7075
this.tail = node;
71-
node.next = this.head;
72-
this.head = node;
76+
}
77+
else {
78+
node.next = this.head;
79+
this.head = node;
80+
}
7381
this._length++;
7482
return this.head;
7583
}
7684
shift() {
7785
// remove first node from list
7886
// O(1)
7987
if (!this.head)
80-
returnnull;
88+
return;
8189
const newHead = this.head.next;
8290
const oldHead = this.head;
8391
oldHead.next = null;
@@ -88,8 +96,8 @@ class SinglyLinkedList {
8896
return oldHead;
8997
}
9098
get(index) {
91-
if (!this.head || (index > this.length -1|| index < 0))
92-
returnnull;
99+
if (!this.head || (index > this.length || index < 0))
100+
thrownewIndexError_1.default;
93101
let counter = 0;
94102
let node = this.head;
95103
while (counter < index) {
@@ -108,8 +116,10 @@ class SinglyLinkedList {
108116
return selectedNode;
109117
}
110118
insert(value, index, options = { prevEnabled: false }) {
111-
if (index > this.length - 1 || index < 0)
112-
return null; // TODO: throw indexError ? user tried to insert at out of range index.
119+
if (!index)
120+
return this.push(value);
121+
if (index > this.length || index < 0)
122+
throw new IndexError_1.default;
113123
if (index === 0)
114124
return this.unshift(value);
115125
if (index === this.length)
@@ -129,8 +139,8 @@ class SinglyLinkedList {
129139
return newNode;
130140
}
131141
removeIndex(index, options = { prevEnabled: false }) {
132-
if (index > this.length -1|| index < 0)
133-
returnnull;
142+
if (index > this.length || index < 0)
143+
thrownewIndexError_1.default;
134144
if (index === 0)
135145
return this.shift();
136146
if (index === this.length - 1)
@@ -148,5 +158,35 @@ class SinglyLinkedList {
148158
this._length--;
149159
return targetNode;
150160
}
161+
log(beginning = 0, end = this.length - 1) {
162+
if ((beginning > this.length - 1 || beginning < 0)
163+
|| (end > this.length - 1 || end < 0))
164+
throw new IndexError_1.default;
165+
let node = this.get(beginning);
166+
let count = beginning;
167+
while (node) {
168+
console.log(`node ${count}`, node);
169+
if (node.next) {
170+
node = node.next;
171+
count++;
172+
}
173+
else
174+
return;
175+
}
176+
}
177+
toString(beginning = 0, end = this.length - 1) {
178+
if ((beginning > this.length - 1 || beginning < 0)
179+
|| (end > this.length - 1 || end < 0))
180+
throw new IndexError_1.default;
181+
let node = this.get(beginning);
182+
while (node) {
183+
console.log(node.data);
184+
if (node.next) {
185+
node = node.next;
186+
}
187+
else
188+
return;
189+
}
190+
}
151191
}
152192
exports.default = SinglyLinkedList;
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
"use strict";
2+
var __importDefault = (this && this.__importDefault) || function (mod) {
3+
return (mod && mod.__esModule) ? mod : { "default": mod };
4+
};
5+
Object.defineProperty(exports, "__esModule", { value: true });
6+
const SinglyLinkedList_1 = __importDefault(require("./SinglyLinkedList"));
7+
function testTraverseSLL(list) {
8+
if (!list.head)
9+
return true; // empty list
10+
let node = list.head;
11+
let countedLength = 1;
12+
while (node) {
13+
// console.log(node.data)
14+
if (node.next) {
15+
node = node.next;
16+
countedLength++;
17+
}
18+
else
19+
break;
20+
}
21+
return (node === list.tail && countedLength === list.length);
22+
}
23+
exports.testTraverseSLL = testTraverseSLL;
24+
test('popping from an empty list returns void', () => {
25+
const list = new SinglyLinkedList_1.default();
26+
expect(list.pop()).toBeUndefined();
27+
});
28+
test('shifting from an empty list returns void', () => {
29+
const list = new SinglyLinkedList_1.default();
30+
expect(list.shift()).toBeUndefined();
31+
});
32+
test('pushed node to empty list, node -> new head & tail', () => {
33+
const list = new SinglyLinkedList_1.default();
34+
list.push('value');
35+
expect(list.head.data === 'value' && list.tail.data === 'value').toBe(true);
36+
});
37+
test('unshifting node to empty list, node -> new head & tail', () => {
38+
const list = new SinglyLinkedList_1.default();
39+
list.unshift('value');
40+
expect(list.head.data === 'value' && list.tail.data === 'value').toBe(true);
41+
});
42+
test('insert as 0, or without index, on empty list, node -> new head & tail', () => {
43+
const list = new SinglyLinkedList_1.default();
44+
list.insert('value', 0);
45+
expect(list.head.data === 'value' && list.tail.data === 'value').toBe(true);
46+
});
47+
test('Able to insert in the middle of the list and maintain traversal', () => {
48+
const list = new SinglyLinkedList_1.default();
49+
list.push('Van Gogh');
50+
list.unshift('Matisse');
51+
list.push('Gauguin');
52+
list.unshift('Picasso');
53+
list.insert('Toulouse-Lautrec', 1);
54+
expect(testTraverseSLL(list)).toBe(true);
55+
});
56+
test(`Able to traverse a list mutated by pushes, pops, shifts, unshifts, inserts, and removals all the way from head to tail`, () => {
57+
const list = new SinglyLinkedList_1.default();
58+
for (let i = 1; i < 100; i++) {
59+
if (list.length > 1 && list.head.next === null) {
60+
console.log('caught length inconsistency at: ', i);
61+
console.log(list.log());
62+
throw new Error;
63+
}
64+
if (i % 2 === 0) {
65+
list.push(`push: ${i}`);
66+
}
67+
if (i % 3 === 0) {
68+
list.unshift(`unshift: ${i}`);
69+
}
70+
if (i % 5 === 0) {
71+
list.pop();
72+
}
73+
if (i % 7 === 0) {
74+
list.shift();
75+
}
76+
if (i % 9 === 0) {
77+
list.insert(`insert: ${i}`, Math.floor(Math.random() * list.length));
78+
}
79+
if (i % 11 === 0) {
80+
list.removeIndex(Math.floor(Math.random() * list.length));
81+
}
82+
}
83+
expect(testTraverseSLL(list)).toBe(true);
84+
});

‎dist/utils/IndexError.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
"use strict";
2+
Object.defineProperty(exports, "__esModule", { value: true });
3+
class IndexError extends Error {
4+
}
5+
exports.default = IndexError;

‎jest.config.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module.exports = {
2+
rootDir: './dist',
3+
};

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /