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 52a2c21

Browse files
committedAug 28, 2018
Add support for complex data to PriorityQueue
1 parent af64d12 commit 52a2c21

File tree

2 files changed

+132
-77
lines changed

2 files changed

+132
-77
lines changed
 

‎src/data-structures/priority-queue/PriorityQueue.js

+49-24
Original file line numberDiff line numberDiff line change
@@ -4,87 +4,112 @@ import Comparator from '../../utils/comparator/Comparator';
44
// It is the same as min heap except that when comparing to elements
55
// we take into account not element's value but rather its priority.
66
export default class PriorityQueue extends MinHeap {
7-
constructor() {
7+
/**
8+
* @constructor
9+
* @param {function|undefined} compareValueFunction
10+
*/
11+
constructor(compareValueFunction) {
812
super();
9-
this.priorities = {};
13+
// Map data structure supports using any value for key type
14+
// e.g. functions, objects, or primitives
15+
this.priorities = new Map();
1016
this.compare = new Comparator(this.comparePriority.bind(this));
17+
this.compareValue = new Comparator(compareValueFunction);
1118
}
1219

1320
/**
1421
* @param {*} item
15-
* @param {number} [priority]
22+
* @param {number} priority
1623
* @return {PriorityQueue}
1724
*/
1825
add(item, priority = 0) {
19-
this.priorities[item] = priority;
26+
this.priorities.set(item, priority);
2027
super.add(item);
2128

2229
return this;
2330
}
2431

2532
/**
2633
* @param {*} item
27-
* @param {Comparator} [customFindingComparator]
34+
* @param {Comparator|function|undefined} maybeComparator
2835
* @return {PriorityQueue}
2936
*/
30-
remove(item, customFindingComparator) {
31-
super.remove(item, customFindingComparator);
32-
delete this.priorities[item];
37+
remove(item, maybeComparator) {
38+
const comparator = this.getValueComparator(maybeComparator);
39+
super.remove(item, comparator);
40+
this.priorities.delete(item);
3341

3442
return this;
3543
}
3644

3745
/**
3846
* @param {*} item
3947
* @param {number} priority
48+
* @param {Comparator|function|undefined} maybeComparator
4049
* @return {PriorityQueue}
4150
*/
42-
changePriority(item, priority) {
43-
this.remove(item, new Comparator(this.compareValue));
51+
changePriority(item, priority, maybeComparator) {
52+
const comparator = this.getValueComparator(maybeComparator);
53+
this.remove(item, comparator);
4454
this.add(item, priority);
4555

4656
return this;
4757
}
4858

4959
/**
5060
* @param {*} item
61+
* @param {Comparator|function|undefined} maybeComparator
5162
* @return {Number[]}
5263
*/
53-
findByValue(item) {
54-
return this.find(item, new Comparator(this.compareValue));
64+
findByValue(item, maybeComparator) {
65+
const comparator = this.getValueComparator(maybeComparator);
66+
return this.find(item, comparator);
5567
}
5668

5769
/**
5870
* @param {*} item
71+
* @param {Comparator|function|undefined} maybeComparator
5972
* @return {boolean}
6073
*/
61-
hasValue(item) {
62-
return this.findByValue(item).length > 0;
74+
hasValue(item, maybeComparator) {
75+
const comparator = this.getValueComparator(maybeComparator);
76+
return this.findByValue(item, comparator).length > 0;
6377
}
6478

6579
/**
66-
* @param {*} a
67-
* @param {*} b
68-
* @return {number}
80+
* @param {Comparator|function|undefined} maybeComparator
81+
* @return {Comparator}
6982
*/
70-
comparePriority(a, b) {
71-
if (this.priorities[a] === this.priorities[b]) {
72-
return 0;
83+
getValueComparator(maybeComparator) {
84+
if (maybeComparator == null) {
85+
return this.compareValue;
86+
}
87+
88+
if (maybeComparator instanceof Comparator) {
89+
return maybeComparator;
7390
}
7491

75-
return this.priorities[a] < this.priorities[b] ? -1 : 1;
92+
if (maybeComparator instanceof Function) {
93+
return new Comparator(maybeComparator);
94+
}
95+
96+
throw new TypeError(
97+
'Invalid comparator type\n'
98+
+ 'Must be one of: Comparator | Function | undefined\n'
99+
+ `Given: ${typeof maybeComparator}`,
100+
);
76101
}
77102

78103
/**
79104
* @param {*} a
80105
* @param {*} b
81106
* @return {number}
82107
*/
83-
compareValue(a, b) {
84-
if (a === b) {
108+
comparePriority(a, b) {
109+
if (this.priorities.get(a) === this.priorities.get(b)) {
85110
return 0;
86111
}
87112

88-
return a < b ? -1 : 1;
113+
return this.priorities.get(a) < this.priorities.get(b) ? -1 : 1;
89114
}
90115
}
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
import PriorityQueue from '../PriorityQueue';
22

3+
const JOB1 = { type: 'job1' };
4+
const JOB2 = { type: 'job2' };
5+
const JOB3 = { type: 'job3' };
6+
const JOB4 = { type: 'job4' };
7+
const JOB5 = { type: 'job5' };
8+
39
describe('PriorityQueue', () => {
410
it('should create default priority queue', () => {
511
const priorityQueue = new PriorityQueue();
@@ -10,94 +16,118 @@ describe('PriorityQueue', () => {
1016
it('should insert items to the queue and respect priorities', () => {
1117
const priorityQueue = new PriorityQueue();
1218

13-
priorityQueue.add(10, 1);
14-
expect(priorityQueue.peek()).toBe(10);
19+
priorityQueue.add(JOB1, 1);
20+
expect(priorityQueue.peek()).toBe(JOB1);
1521

16-
priorityQueue.add(5, 2);
17-
expect(priorityQueue.peek()).toBe(10);
22+
priorityQueue.add(JOB2, 2);
23+
expect(priorityQueue.peek()).toBe(JOB1);
1824

19-
priorityQueue.add(100, 0);
20-
expect(priorityQueue.peek()).toBe(100);
25+
priorityQueue.add(JOB3, 0);
26+
expect(priorityQueue.peek()).toBe(JOB3);
2127
});
2228

2329
it('should poll from queue with respect to priorities', () => {
2430
const priorityQueue = new PriorityQueue();
2531

26-
priorityQueue.add(10, 1);
27-
priorityQueue.add(5, 2);
28-
priorityQueue.add(100, 0);
29-
priorityQueue.add(200, 0);
32+
priorityQueue.add(JOB1, 1);
33+
priorityQueue.add(JOB2, 2);
34+
priorityQueue.add(JOB3, 0);
35+
priorityQueue.add(JOB4, 0);
3036

31-
expect(priorityQueue.poll()).toBe(100);
32-
expect(priorityQueue.poll()).toBe(200);
33-
expect(priorityQueue.poll()).toBe(10);
34-
expect(priorityQueue.poll()).toBe(5);
37+
expect(priorityQueue.poll()).toBe(JOB3);
38+
expect(priorityQueue.poll()).toBe(JOB4);
39+
expect(priorityQueue.poll()).toBe(JOB1);
40+
expect(priorityQueue.poll()).toBe(JOB2);
3541
});
3642

3743
it('should be possible to change priority of internal nodes', () => {
3844
const priorityQueue = new PriorityQueue();
3945

40-
priorityQueue.add(10, 1);
41-
priorityQueue.add(5, 2);
42-
priorityQueue.add(100, 0);
43-
priorityQueue.add(200, 0);
46+
priorityQueue.add(JOB1, 1);
47+
priorityQueue.add(JOB2, 2);
48+
priorityQueue.add(JOB3, 0);
49+
priorityQueue.add(JOB4, 0);
4450

45-
priorityQueue.changePriority(100, 10);
46-
priorityQueue.changePriority(10, 20);
51+
priorityQueue.changePriority(JOB4, 10);
52+
priorityQueue.changePriority(JOB1, 20);
4753

48-
expect(priorityQueue.poll()).toBe(200);
49-
expect(priorityQueue.poll()).toBe(5);
50-
expect(priorityQueue.poll()).toBe(100);
51-
expect(priorityQueue.poll()).toBe(10);
54+
expect(priorityQueue.poll()).toBe(JOB3);
55+
expect(priorityQueue.poll()).toBe(JOB2);
56+
expect(priorityQueue.poll()).toBe(JOB4);
57+
expect(priorityQueue.poll()).toBe(JOB1);
5258
});
5359

5460
it('should be possible to change priority of head node', () => {
5561
const priorityQueue = new PriorityQueue();
5662

57-
priorityQueue.add(10, 1);
58-
priorityQueue.add(5, 2);
59-
priorityQueue.add(100, 0);
60-
priorityQueue.add(200, 0);
63+
priorityQueue.add(JOB1, 1);
64+
priorityQueue.add(JOB2, 2);
65+
priorityQueue.add(JOB3, 0);
66+
priorityQueue.add(JOB4, 0);
6167

62-
priorityQueue.changePriority(200, 10);
63-
priorityQueue.changePriority(10, 20);
68+
priorityQueue.changePriority(JOB3, 10);
69+
priorityQueue.changePriority(JOB1, 20);
6470

65-
expect(priorityQueue.poll()).toBe(100);
66-
expect(priorityQueue.poll()).toBe(5);
67-
expect(priorityQueue.poll()).toBe(200);
68-
expect(priorityQueue.poll()).toBe(10);
71+
expect(priorityQueue.poll()).toBe(JOB4);
72+
expect(priorityQueue.poll()).toBe(JOB2);
73+
expect(priorityQueue.poll()).toBe(JOB3);
74+
expect(priorityQueue.poll()).toBe(JOB1);
6975
});
7076

7177
it('should be possible to change priority along with node addition', () => {
7278
const priorityQueue = new PriorityQueue();
7379

74-
priorityQueue.add(10, 1);
75-
priorityQueue.add(5, 2);
76-
priorityQueue.add(100, 0);
77-
priorityQueue.add(200, 0);
80+
priorityQueue.add(JOB1, 1);
81+
priorityQueue.add(JOB2, 2);
82+
priorityQueue.add(JOB3, 0);
83+
priorityQueue.add(JOB4, 0);
7884

79-
priorityQueue.changePriority(200, 10);
80-
priorityQueue.changePriority(10, 20);
85+
priorityQueue.changePriority(JOB4, 10);
86+
priorityQueue.changePriority(JOB1, 20);
8187

82-
priorityQueue.add(15, 15);
88+
priorityQueue.add(JOB5, 15);
8389

84-
expect(priorityQueue.poll()).toBe(100);
85-
expect(priorityQueue.poll()).toBe(5);
86-
expect(priorityQueue.poll()).toBe(200);
87-
expect(priorityQueue.poll()).toBe(15);
88-
expect(priorityQueue.poll()).toBe(10);
90+
expect(priorityQueue.poll()).toBe(JOB3);
91+
expect(priorityQueue.poll()).toBe(JOB2);
92+
expect(priorityQueue.poll()).toBe(JOB4);
93+
expect(priorityQueue.poll()).toBe(JOB5);
94+
expect(priorityQueue.poll()).toBe(JOB1);
8995
});
9096

9197
it('should be possible to search in priority queue by value', () => {
9298
const priorityQueue = new PriorityQueue();
9399

94-
priorityQueue.add(10, 1);
95-
priorityQueue.add(5, 2);
96-
priorityQueue.add(100, 0);
97-
priorityQueue.add(200, 0);
98-
priorityQueue.add(15, 15);
100+
priorityQueue.add(JOB1, 1);
101+
priorityQueue.add(JOB2, 2);
102+
priorityQueue.add(JOB3, 0);
103+
priorityQueue.add(JOB4, 0);
104+
priorityQueue.add(JOB5, 15);
105+
106+
const job6 = { type: 'job6' };
107+
108+
expect(priorityQueue.hasValue(job6)).toBe(false);
109+
expect(priorityQueue.hasValue(JOB5)).toBe(true);
110+
});
111+
112+
it('should accept a custom compareValue function', () => {
113+
const compareByType = (a, b) => {
114+
if (a.type === b.type) {
115+
return 0;
116+
}
117+
118+
return a.type < b.type ? -1 : 1;
119+
};
120+
121+
const priorityQueue = new PriorityQueue(compareByType);
122+
123+
priorityQueue.add(JOB1, 1);
124+
priorityQueue.add(JOB2, 2);
125+
priorityQueue.add(JOB3, 0);
126+
127+
const existingJobType = { type: 'job1' };
128+
const newJobType = { type: 'job4' };
99129

100-
expect(priorityQueue.hasValue(70)).toBe(false);
101-
expect(priorityQueue.hasValue(15)).toBe(true);
130+
expect(priorityQueue.hasValue(existingJobType)).toBe(true);
131+
expect(priorityQueue.hasValue(newJobType)).toBe(false);
102132
});
103133
});

0 commit comments

Comments
 (0)
Please sign in to comment.