-
Notifications
You must be signed in to change notification settings - Fork 4
/
BloomFilter.js
131 lines (112 loc) · 2.92 KB
/
BloomFilter.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
export default class BloomFilter {
/**
* @param {number} size - the size of the storage.
*/
constructor(size = 100) {
// Bloom filter size directly affects the likelihood of false positives.
// The bigger the size the lower the likelihood of false positives.
this.size = size;
this.storage = this.createStore(size);
}
/**
* @param {string} item
*/
insert(item) {
const hashValues = this.getHashValues(item);
// Set each hashValue index to true.
hashValues.forEach(val => this.storage.setValue(val));
}
/**
* @param {string} item
* @return {boolean}
*/
mayContain(item) {
const hashValues = this.getHashValues(item);
for (let hashIndex = 0; hashIndex < hashValues.length; hashIndex += 1) {
if (!this.storage.getValue(hashValues[hashIndex])) {
// We know that the item was definitely not inserted.
return false;
}
}
// The item may or may not have been inserted.
return true;
}
/**
* Creates the data store for our filter.
* We use this method to generate the store in order to
* encapsulate the data itself and only provide access
* to the necessary methods.
*
* @param {number} size
* @return {Object}
*/
createStore(size) {
const storage = [];
// Initialize all indexes to false
for (let storageCellIndex = 0; storageCellIndex < size; storageCellIndex += 1) {
storage.push(false);
}
const storageInterface = {
getValue(index) {
return storage[index];
},
setValue(index) {
storage[index] = true;
},
};
return storageInterface;
}
/**
* @param {string} item
* @return {number}
*/
hash1(item) {
let hash = 0;
for (let charIndex = 0; charIndex < item.length; charIndex += 1) {
const char = item.charCodeAt(charIndex);
hash = (hash << 5) + hash + char;
hash &= hash; // Convert to 32bit integer
hash = Math.abs(hash);
}
return hash % this.size;
}
/**
* @param {string} item
* @return {number}
*/
hash2(item) {
let hash = 5381;
for (let charIndex = 0; charIndex < item.length; charIndex += 1) {
const char = item.charCodeAt(charIndex);
hash = (hash << 5) + hash + char; /* hash * 33 + c */
}
return Math.abs(hash % this.size);
}
/**
* @param {string} item
* @return {number}
*/
hash3(item) {
let hash = 0;
for (let charIndex = 0; charIndex < item.length; charIndex += 1) {
const char = item.charCodeAt(charIndex);
hash = (hash << 5) - hash;
hash += char;
hash &= hash; // Convert to 32bit integer
}
return Math.abs(hash % this.size);
}
/**
* Runs all 3 hash functions on the input and returns an array of results.
*
* @param {string} item
* @return {number[]}
*/
getHashValues(item) {
return [
this.hash1(item),
this.hash2(item),
this.hash3(item),
];
}
}