Skip to content

Commit 8c46dbf

Browse files
committedApr 9, 2018
Make it possible to add objects to LinkedList.
1 parent cdf7220 commit 8c46dbf

File tree

10 files changed

+204
-189
lines changed

10 files changed

+204
-189
lines changed
 

‎src/data-structures/hash-table/HashTable.js

+19-3
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,32 @@ export default class HashTable {
2121

2222
insert(key, value) {
2323
const bucketLinkedList = this.buckets[this.hash(key)];
24-
bucketLinkedList.appendUnique({ key, value });
24+
const node = bucketLinkedList.find({ callback: nodeValue => nodeValue.key === key });
25+
26+
if (!node) {
27+
// Insert new node.
28+
bucketLinkedList.append({ key, value });
29+
} else {
30+
// Update value of existing node.
31+
node.value.value = value;
32+
}
2533
}
2634

2735
delete(key) {
2836
const bucketLinkedList = this.buckets[this.hash(key)];
29-
return bucketLinkedList.deleteByKey(key);
37+
const node = bucketLinkedList.find({ callback: nodeValue => nodeValue.key === key });
38+
39+
if (node) {
40+
return bucketLinkedList.delete(node.value);
41+
}
42+
43+
return null;
3044
}
3145

3246
get(key) {
3347
const bucketLinkedList = this.buckets[this.hash(key)];
34-
return bucketLinkedList.findByKey(key);
48+
const node = bucketLinkedList.find({ callback: nodeValue => nodeValue.key === key });
49+
50+
return node ? node.value.value : null;
3551
}
3652
}

‎src/data-structures/hash-table/__test__/HashTable.test.js

+22-7
Original file line numberDiff line numberDiff line change
@@ -31,19 +31,34 @@ describe('HashTable', () => {
3131
hashTable.insert('c', 'earth');
3232
hashTable.insert('d', 'ocean');
3333

34-
expect(hashTable.buckets[0].toString()).toBe('c:earth');
35-
expect(hashTable.buckets[1].toString()).toBe('a:sky,d:ocean');
36-
expect(hashTable.buckets[2].toString()).toBe('b:sea');
34+
const stringifier = value => `${value.key}:${value.value}`;
3735

38-
expect(hashTable.get('a').value).toBe('sky');
39-
expect(hashTable.get('d').value).toBe('ocean');
36+
expect(hashTable.buckets[0].toString(stringifier)).toBe('c:earth');
37+
expect(hashTable.buckets[1].toString(stringifier)).toBe('a:sky,d:ocean');
38+
expect(hashTable.buckets[2].toString(stringifier)).toBe('b:sea');
39+
40+
expect(hashTable.get('a')).toBe('sky');
41+
expect(hashTable.get('d')).toBe('ocean');
4042

4143
hashTable.delete('a');
4244

45+
expect(hashTable.delete('not-existing')).toBeNull();
46+
4347
expect(hashTable.get('a')).toBeNull();
44-
expect(hashTable.get('d').value).toBe('ocean');
48+
expect(hashTable.get('d')).toBe('ocean');
4549

4650
hashTable.insert('d', 'ocean-new');
47-
expect(hashTable.get('d').value).toBe('ocean-new');
51+
expect(hashTable.get('d')).toBe('ocean-new');
52+
});
53+
54+
it('should be possible to add objects to hash table', () => {
55+
const hashTable = new HashTable();
56+
57+
hashTable.insert('objectKey', { prop1: 'a', prop2: 'b' });
58+
59+
const object = hashTable.get('objectKey');
60+
expect(object).toBeDefined();
61+
expect(object.prop1).toBe('a');
62+
expect(object.prop2).toBe('b');
4863
});
4964
});

‎src/data-structures/linked-list/LinkedList.js

+27-88
Original file line numberDiff line numberDiff line change
@@ -2,74 +2,39 @@ import LinkedListNode from './LinkedListNode';
22

33
export default class LinkedList {
44
constructor() {
5+
/** @var LinkedListNode */
56
this.head = null;
7+
8+
/** @var LinkedListNode */
69
this.tail = null;
710
}
811

9-
prepend({ value, key = null }) {
10-
const newNode = new LinkedListNode({ value, key, next: this.head });
11-
12+
prepend(value) {
1213
// Make new node to be a head.
13-
this.head = newNode;
14+
this.head = new LinkedListNode(value, this.head);
1415

15-
return newNode;
16+
return this;
1617
}
1718

18-
append({ value, key = null }) {
19-
const newNode = new LinkedListNode({ value, key });
19+
append(value) {
20+
const newNode = new LinkedListNode(value);
2021

2122
// If there is no head yet let's make new node a head.
2223
if (!this.head) {
2324
this.head = newNode;
2425
this.tail = newNode;
2526

26-
return newNode;
27+
return this;
2728
}
2829

2930
// Attach new node to the end of linked list.
3031
this.tail.next = newNode;
3132
this.tail = newNode;
3233

33-
return newNode;
34+
return this;
3435
}
3536

36-
appendUnique({ value, key = null }) {
37-
const newNode = new LinkedListNode({ value, key });
38-
39-
// If there is no head yet let's make new node a head.
40-
if (!this.head) {
41-
this.head = newNode;
42-
this.tail = newNode;
43-
44-
return newNode;
45-
}
46-
47-
// Rewind to last node.
48-
let currentNode = this.head;
49-
while (currentNode.next !== null) {
50-
// If there is a node with specified key exists then update it instead of adding new one.
51-
if (key && currentNode.key === key) {
52-
currentNode.value = value;
53-
return currentNode;
54-
}
55-
56-
currentNode = currentNode.next;
57-
}
58-
59-
// If there is a node with specified key exists then update it instead of adding new one.
60-
if (key && currentNode.key === key) {
61-
currentNode.value = value;
62-
return currentNode;
63-
}
64-
65-
// Attach new node to the end of linked list.
66-
currentNode.next = newNode;
67-
this.tail = newNode;
68-
69-
return newNode;
70-
}
71-
72-
deleteByValue(value) {
37+
delete(value) {
7338
if (!this.head) {
7439
return null;
7540
}
@@ -102,37 +67,28 @@ export default class LinkedList {
10267
return deletedNode;
10368
}
10469

105-
deleteByKey(key) {
70+
find({ value = undefined, callback = undefined }) {
10671
if (!this.head) {
10772
return null;
10873
}
10974

110-
let deletedNode = null;
111-
112-
// If the head must be deleted then make 2nd node to be a head.
113-
if (this.head.key === key) {
114-
deletedNode = this.head;
115-
this.head = this.head.next;
116-
}
117-
11875
let currentNode = this.head;
11976

120-
// If next node must be deleted then make next node to be a next next one.
121-
while (currentNode.next) {
122-
if (currentNode.next.key === key) {
123-
deletedNode = currentNode.next;
124-
currentNode.next = currentNode.next.next;
125-
} else {
126-
currentNode = currentNode.next;
77+
while (currentNode) {
78+
// If callback is specified then try to find node by callback.
79+
if (callback && callback(currentNode.value)) {
80+
return currentNode;
12781
}
128-
}
12982

130-
// Check if tail must be deleted.
131-
if (this.tail.key === key) {
132-
this.tail = currentNode;
83+
// If value is specified then try to compare by value..
84+
if (value !== undefined && currentNode.value === value) {
85+
return currentNode;
86+
}
87+
88+
currentNode = currentNode.next;
13389
}
13490

135-
return deletedNode;
91+
return null;
13692
}
13793

13894
deleteTail() {
@@ -177,32 +133,15 @@ export default class LinkedList {
177133
return deletedHead;
178134
}
179135

180-
findByKey(key) {
181-
let currentNode = this.head;
136+
toString(callback) {
137+
const nodeStrings = [];
182138

183-
while (currentNode) {
184-
if (currentNode.key === key) {
185-
return currentNode;
186-
}
187-
currentNode = currentNode.next;
188-
}
189-
190-
return null;
191-
}
192-
193-
toArray() {
194-
const listArray = [];
195139
let currentNode = this.head;
196-
197140
while (currentNode) {
198-
listArray.push(currentNode.toString());
141+
nodeStrings.push(currentNode.toString(callback));
199142
currentNode = currentNode.next;
200143
}
201144

202-
return listArray;
203-
}
204-
205-
toString() {
206-
return this.toArray().toString();
145+
return nodeStrings.toString();
207146
}
208147
}
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,10 @@
11
export default class LinkedListNode {
2-
constructor({ value, next = null, key = null }) {
2+
constructor(value, next = null) {
33
this.value = value;
44
this.next = next;
5-
6-
// Key is added to make this linked list nodes to be reusable in hash tables.
7-
this.key = key;
85
}
96

10-
toString() {
11-
if (this.key) {
12-
return `${this.key}:${this.value}`;
13-
}
14-
15-
return `${this.value}`;
7+
toString(callback) {
8+
return callback ? callback(this.value) : `${this.value}`;
169
}
1710
}

‎src/data-structures/linked-list/__test__/LinkedList.test.js

+61-71
Original file line numberDiff line numberDiff line change
@@ -12,105 +12,69 @@ describe('LinkedList', () => {
1212
expect(linkedList.head).toBeNull();
1313
expect(linkedList.tail).toBeNull();
1414

15-
const node1 = linkedList.append({ value: 1 });
16-
const node2 = linkedList.append({ value: 2, key: 'test' });
15+
linkedList.append(1);
16+
linkedList.append(2);
1717

18-
expect(node1.value).toBe(1);
19-
expect(node2.value).toBe(2);
20-
expect(node2.key).toBe('test');
21-
22-
expect(linkedList.head.toString()).toBe('1');
23-
expect(linkedList.tail.toString()).toBe('test:2');
24-
25-
expect(linkedList.toString()).toBe('1,test:2');
18+
expect(linkedList.toString()).toBe('1,2');
2619
});
2720

2821
it('should prepend node to linked list', () => {
2922
const linkedList = new LinkedList();
3023

31-
const node1 = linkedList.append({ value: 1 });
32-
const node2 = linkedList.prepend({ value: 2 });
33-
34-
expect(node1.value).toBe(1);
35-
expect(node2.value).toBe(2);
36-
37-
expect(linkedList.head.toString()).toBe('2');
38-
expect(linkedList.tail.toString()).toBe('1');
24+
linkedList.append(1);
25+
linkedList.prepend(2);
3926

4027
expect(linkedList.toString()).toBe('2,1');
4128
});
4229

4330
it('should delete node by value from linked list', () => {
4431
const linkedList = new LinkedList();
4532

46-
expect(linkedList.deleteByValue(5)).toBeNull();
33+
expect(linkedList.delete(5)).toBeNull();
4734

48-
linkedList.append({ value: 1 });
49-
linkedList.append({ value: 2 });
50-
linkedList.append({ value: 3 });
51-
linkedList.append({ value: 3 });
52-
linkedList.append({ value: 3 });
53-
linkedList.append({ value: 4 });
54-
linkedList.append({ value: 5 });
35+
linkedList.append(1);
36+
linkedList.append(2);
37+
linkedList.append(3);
38+
linkedList.append(3);
39+
linkedList.append(3);
40+
linkedList.append(4);
41+
linkedList.append(5);
5542

5643
expect(linkedList.head.toString()).toBe('1');
5744
expect(linkedList.tail.toString()).toBe('5');
5845

59-
const deletedNode = linkedList.deleteByValue(3);
46+
const deletedNode = linkedList.delete(3);
6047
expect(deletedNode.value).toBe(3);
6148
expect(linkedList.toString()).toBe('1,2,4,5');
6249

63-
linkedList.deleteByValue(3);
50+
linkedList.delete(3);
6451
expect(linkedList.toString()).toBe('1,2,4,5');
6552

66-
linkedList.deleteByValue(1);
53+
linkedList.delete(1);
6754
expect(linkedList.toString()).toBe('2,4,5');
6855

6956
expect(linkedList.head.toString()).toBe('2');
7057
expect(linkedList.tail.toString()).toBe('5');
7158

72-
linkedList.deleteByValue(5);
59+
linkedList.delete(5);
7360
expect(linkedList.toString()).toBe('2,4');
7461

7562
expect(linkedList.head.toString()).toBe('2');
7663
expect(linkedList.tail.toString()).toBe('4');
7764

78-
linkedList.deleteByValue(4);
65+
linkedList.delete(4);
7966
expect(linkedList.toString()).toBe('2');
8067

8168
expect(linkedList.head.toString()).toBe('2');
8269
expect(linkedList.tail.toString()).toBe('2');
8370
});
8471

85-
it('should delete node by key from linked list', () => {
86-
const linkedList = new LinkedList();
87-
88-
expect(linkedList.deleteByKey('key')).toBeNull();
89-
90-
linkedList.append({ value: 1, key: 'test1' });
91-
linkedList.append({ value: 2, key: 'test2' });
92-
linkedList.append({ value: 3, key: 'test3' });
93-
linkedList.append({ value: 4, key: 'test4' });
94-
95-
const deletedNode1 = linkedList.deleteByKey('test2');
96-
expect(deletedNode1.key).toBe('test2');
97-
expect(linkedList.toString()).toBe('test1:1,test3:3,test4:4');
98-
99-
const deletedNode2 = linkedList.deleteByKey('test1');
100-
expect(deletedNode2.key).toBe('test1');
101-
expect(linkedList.toString()).toBe('test3:3,test4:4');
102-
103-
const deletedNode3 = linkedList.deleteByKey('test4');
104-
expect(deletedNode3.key).toBe('test4');
105-
expect(linkedList.toString()).toBe('test3:3');
106-
});
107-
10872
it('should delete linked list tail', () => {
10973
const linkedList = new LinkedList();
11074

111-
linkedList.append({ value: 1 });
112-
linkedList.append({ value: 2 });
113-
linkedList.append({ value: 3 });
75+
linkedList.append(1);
76+
linkedList.append(2);
77+
linkedList.append(3);
11478

11579
expect(linkedList.head.toString()).toBe('1');
11680
expect(linkedList.tail.toString()).toBe('3');
@@ -142,8 +106,8 @@ describe('LinkedList', () => {
142106

143107
expect(linkedList.deleteHead()).toBeNull();
144108

145-
linkedList.append({ value: 1 });
146-
linkedList.append({ value: 2 });
109+
linkedList.append(1);
110+
linkedList.append(2);
147111

148112
expect(linkedList.head.toString()).toBe('1');
149113
expect(linkedList.tail.toString()).toBe('2');
@@ -163,26 +127,52 @@ describe('LinkedList', () => {
163127
expect(linkedList.tail).toBeNull();
164128
});
165129

166-
it('should append unique nodes', () => {
130+
it('should be possible to store objects in the list and to print them out', () => {
167131
const linkedList = new LinkedList();
168132

169-
linkedList.appendUnique({ value: 1, key: 'test1' });
170-
linkedList.appendUnique({ value: 2, key: 'test2' });
171-
linkedList.appendUnique({ value: 3, key: 'test2' });
172-
linkedList.appendUnique({ value: 5, key: 'test1' });
133+
const nodeValue1 = { value: 1, key: 'key1' };
134+
const nodeValue2 = { value: 2, key: 'key2' };
135+
136+
linkedList
137+
.append(nodeValue1)
138+
.prepend(nodeValue2);
139+
140+
const nodeStringifier = value => `${value.key}:${value.value}`;
141+
142+
expect(linkedList.toString(nodeStringifier)).toBe('key2:2,key1:1');
143+
});
144+
145+
it('should find node by value', () => {
146+
const linkedList = new LinkedList();
147+
148+
expect(linkedList.find({ value: 5 })).toBeNull();
149+
150+
linkedList.append(1);
151+
expect(linkedList.find({ value: 1 })).toBeDefined();
152+
153+
linkedList
154+
.append(2)
155+
.append(3);
156+
157+
const node = linkedList.find({ value: 2 });
173158

174-
expect(linkedList.toString()).toBe('test1:5,test2:3');
159+
expect(node.value).toBe(2);
160+
expect(linkedList.find({ value: 5 })).toBeNull();
175161
});
176162

177-
it('should find node by its key', () => {
163+
it('should find node by callback', () => {
178164
const linkedList = new LinkedList();
179165

180-
expect(linkedList.findByKey('test')).toBeNull();
166+
linkedList
167+
.append({ value: 1, key: 'test1' })
168+
.append({ value: 2, key: 'test2' })
169+
.append({ value: 3, key: 'test3' });
181170

182-
linkedList.appendUnique({ value: 1, key: 'test1' });
183-
linkedList.appendUnique({ value: 2, key: 'test2' });
184-
linkedList.appendUnique({ value: 3, key: 'test3' });
171+
const node = linkedList.find({ callback: value => value.key === 'test2' });
185172

186-
expect(linkedList.findByKey('test3').toString()).toBe('test3:3');
173+
expect(node).toBeDefined();
174+
expect(node.value.value).toBe(2);
175+
expect(node.value.key).toBe('test2');
176+
expect(linkedList.find({ callback: value => value.key === 'test5' })).toBeNull();
187177
});
188178
});
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,46 @@
11
import LinkedListNode from '../LinkedListNode';
22

33
describe('LinkedListNode', () => {
4-
it('should create list node with kay and value', () => {
5-
const node = new LinkedListNode({ value: 1, key: 'test' });
4+
it('should create list node with value', () => {
5+
const node = new LinkedListNode(1);
6+
67
expect(node.value).toBe(1);
7-
expect(node.key).toBe('test');
88
expect(node.next).toBeNull();
99
});
1010

11+
it('should create list node with object as a value', () => {
12+
const nodeValue = { value: 1, key: 'test' };
13+
const node = new LinkedListNode(nodeValue);
14+
15+
expect(node.value.value).toBe(1);
16+
expect(node.value.key).toBe('test');
17+
expect(node.next).toBeNull();
18+
});
19+
20+
it('should link nodes together', () => {
21+
const node2 = new LinkedListNode(2);
22+
const node1 = new LinkedListNode(1, node2);
23+
24+
expect(node1.next).toBeDefined();
25+
expect(node2.next).toBeNull();
26+
expect(node1.value).toBe(1);
27+
expect(node1.next.value).toBe(2);
28+
});
29+
1130
it('should convert node to string', () => {
12-
const node = new LinkedListNode({ value: 1 });
13-
const nodeWithKey = new LinkedListNode({ value: 1, key: 'test' });
31+
const node = new LinkedListNode(1);
1432

1533
expect(node.toString()).toBe('1');
16-
expect(nodeWithKey.toString()).toBe('test:1');
34+
35+
node.value = 'string value';
36+
expect(node.toString()).toBe('string value');
37+
});
38+
39+
it('should convert node to string with custom stringifier', () => {
40+
const nodeValue = { value: 1, key: 'test' };
41+
const node = new LinkedListNode(nodeValue);
42+
const toStringCallback = value => `value: ${value.value}, key: ${value.key}`;
43+
44+
expect(node.toString(toStringCallback)).toBe('value: 1, key: test');
1745
});
1846
});

‎src/data-structures/queue/Queue.js

+5-1
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,15 @@ export default class Queue {
1818
}
1919

2020
enqueue(value) {
21-
this.linkedList.append({ value });
21+
this.linkedList.append(value);
2222
}
2323

2424
dequeue() {
2525
const removedHead = this.linkedList.deleteHead();
2626
return removedHead ? removedHead.value : null;
2727
}
28+
29+
toString(callback) {
30+
return this.linkedList.toString(callback);
31+
}
2832
}

‎src/data-structures/queue/__test__/Queue.test.js

+14-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,20 @@ describe('Queue', () => {
1313
queue.enqueue(1);
1414
queue.enqueue(2);
1515

16-
expect(queue.linkedList.toString()).toBe('1,2');
16+
expect(queue.toString()).toBe('1,2');
17+
});
18+
19+
it('should be possible to enqueue/dequeue objects', () => {
20+
const queue = new Queue();
21+
22+
queue.enqueue({ value: 'test1', key: 'key1' });
23+
queue.enqueue({ value: 'test2', key: 'key2' });
24+
25+
const stringifier = value => `${value.key}:${value.value}`;
26+
27+
expect(queue.toString(stringifier)).toBe('key1:test1,key2:test2');
28+
expect(queue.dequeue().value).toBe('test1');
29+
expect(queue.dequeue().value).toBe('test2');
1730
});
1831

1932
it('should peek data from queue', () => {

‎src/data-structures/stack/Stack.js

+5-1
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,15 @@ export default class Stack {
1818
}
1919

2020
push(value) {
21-
this.linkedList.append({ value });
21+
this.linkedList.append(value);
2222
}
2323

2424
pop() {
2525
const removedTail = this.linkedList.deleteTail();
2626
return removedTail ? removedTail.value : null;
2727
}
28+
29+
toString(callback) {
30+
return this.linkedList.toString(callback);
31+
}
2832
}

‎src/data-structures/stack/__test__/Stack.test.js

+14-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ describe('Stack', () => {
1313
stack.push(1);
1414
stack.push(2);
1515

16-
expect(stack.linkedList.toString()).toBe('1,2');
16+
expect(stack.toString()).toBe('1,2');
1717
});
1818

1919
it('should peek data from stack', () => {
@@ -49,4 +49,17 @@ describe('Stack', () => {
4949
expect(stack.pop()).toBeNull();
5050
expect(stack.isEmpty()).toBeTruthy();
5151
});
52+
53+
it('should be possible to push/pop objects', () => {
54+
const stack = new Stack();
55+
56+
stack.push({ value: 'test1', key: 'key1' });
57+
stack.push({ value: 'test2', key: 'key2' });
58+
59+
const stringifier = value => `${value.key}:${value.value}`;
60+
61+
expect(stack.toString(stringifier)).toBe('key1:test1,key2:test2');
62+
expect(stack.pop().value).toBe('test2');
63+
expect(stack.pop().value).toBe('test1');
64+
});
5265
});

0 commit comments

Comments
 (0)
Please sign in to comment.