-
Notifications
You must be signed in to change notification settings - Fork 10
/
Copy pathindex.js
173 lines (147 loc) · 4.56 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
const DEFAULT_HASH_TABLE_SIZE = 32;
export default class HashTable {
/**
* initialize the hash table
*
* hash table size/capacity directly affects on the number of collisions
* the bigger the hash table size/capacity the less collisions we'll get
* for demonstration purposes our DEFAULT_HASH_TABLE_SIZE constant
* is small to show how collisions are being handled.
*
* @param {number} [capacity=DEFAULT_HASH_TABLE_SIZE] the capacity of the table
* @param {Boolean} [simple=true] the type of table
*/
constructor(capacity = DEFAULT_HASH_TABLE_SIZE, simple = true) {
// set the hash table to simple or complex
this.simple = simple;
// let's just have a copy of the capacity of the hash table
this.capacity = capacity;
// create hash table of the given or default capacity.
// to handle collisions well, a linked list could be used to initialize the cells array
this.cells = new Array(capacity);
// just to keep track of all actual keys in a fast way.
this.keys = {};
}
/**
* converts a string key to hash number
*
* in this simple hash function, for simplicity's sake we will use
* character codes sum of all characters of the key to calculate the hash.
*
* @param {string} key
* @return {number}
*/
simpleHash(key) {
const hash = Array
.from(key)
.reduce((finalHash, character) => (finalHash + character.charCodeAt(0)), 0);
// reduce hash number so that it fits the hash table capacity.
return hash % this.capacity;
}
/* eslint-disable no-restricted-properties */
/**
* converts key string to hash number
*
* in this better hash function, we will use a more sophisticated
* approach, polynomial string hash to reduce the number of collisions:
*
* hash = charCodeAt(0) * PRIME^(n-1) + charCodeAt(1) * PRIME^(n-2) + ... + charCodeAt(n-1)
*
* where charCodeAt(i) is the i-th character code of the key, n is the
* length of the key and PRIME is just any prime number like 17.
*
* @param {string} key
* @return {number}
*/
betterHash(key) {
const PRIME = 73;
const hash = Array
.from(key)
.reduce((finalHash, character, index) => {
return (finalHash * Math.pow(PRIME, key.length - index) + character.charCodeAt(0));
}, 0);
// reduce hash number so that it fits the hash table capacity.
return hash % this.capacity;
}
/**
* decides which hash function to use based on the simplicity selected
* @param {string} key
* @return {function}
*/
hash(key) {
return this.simple ? this.simpleHash(key) : this.betterHash(key);
}
/**
* inserts a value into the hash table using its key
* @param {string} key
* @param {any} value
* @return {void}
*/
insert(key, value) {
const keyHash = this.hash(key);
// to handle collisions well, a linked list node could be used here
if (!this.cells[keyHash]) this.cells[keyHash] = {};
if (this.cells[keyHash].key !== key) this.keys[key] = keyHash;
this.cells[keyHash][key] = value;
}
/**
* delete a key and its value from the table
* @param {string} key
* @return {void}
*/
remove(key) {
const keyHash = this.hash(key);
if (this.cells[keyHash] && this.cells[keyHash][key]) {
delete this.cells[keyHash][key];
delete this.keys[key];
return keyHash;
}
return null;
}
/**
* search and retrieve the value of a key given the key
* @param {string} key
* @return {any}
*/
retrieve(key) {
const keyHash = this.hash(key);
if (this.cells[keyHash] && this.cells[keyHash][key]) return this.cells[keyHash][key];
return null;
}
/**
* check whether the has table has a particular key
* @param {string} key
* @return {Boolean}
*/
has(key) {
return Object.keys(this.keys).includes(key);
}
/**
* get the length pf the table i.e. the spaces currently occupied
* NOT the capacity
* @return {nuber}
*/
length() {
return this.cells.reduce((sum, cell) => cell !== null ? sum + 1 : sum + 0, 0);
}
/**
* get all the keys currently contained in the hash table
* @return {array} [description]
*/
getKeys() {
console.log(Object.keys(this.keys));
return Object.keys(this.keys);
}
/**
* print the data in the hash table
* @return {[type]} [description]
*/
print() {
let string = '';
this.cells.forEach((data, i) => {
if (data !== undefined) string += `${i}: ${JSON.stringify(data)}\n`;
});
console.log(string.trim());
return JSON.stringify(this.cells);
}
}