BloomFilter minor fixes.

This commit is contained in:
Oleksii Trekhleb 2018-06-30 20:39:46 +03:00
parent b33b1fe1bc
commit 610b120e27
2 changed files with 64 additions and 37 deletions

View File

@ -1,6 +1,6 @@
export default class BloomFilter {
/**
* @param {number} size
* @param {number} size - the size of the storage.
*/
constructor(size = 100) {
// Bloom filter size directly affects the likelihood of false positives.
@ -15,7 +15,7 @@ export default class BloomFilter {
insert(item) {
const hashValues = this.getHashValues(item);
// Set each hashValue index to true
// Set each hashValue index to true.
hashValues.forEach(val => this.storage.setValue(val));
}
@ -26,8 +26,8 @@ export default class BloomFilter {
mayContain(item) {
const hashValues = this.getHashValues(item);
for (let i = 0; i < hashValues.length; i += 1) {
if (!this.storage.getValue(hashValues[i])) {
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;
}
@ -50,7 +50,7 @@ export default class BloomFilter {
const storage = [];
// Initialize all indexes to false
for (let i = 0; i < size; i += 1) {
for (let storageCellIndex = 0; storageCellIndex < size; storageCellIndex += 1) {
storage.push(false);
}
@ -67,14 +67,14 @@ export default class BloomFilter {
}
/**
* @param {string} str
* @param {string} item
* @return {number}
*/
hash1(str) {
hash1(item) {
let hash = 0;
for (let i = 0; i < str.length; i += 1) {
const char = str.charCodeAt(i);
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);
@ -84,44 +84,48 @@ export default class BloomFilter {
}
/**
* @param {string} str
* @param {string} item
* @return {number}
*/
hash2(str) {
hash2(item) {
let hash = 5381;
for (let i = 0; i < str.length; i += 1) {
const char = str.charCodeAt(i);
for (let charIndex = 0; charIndex < item.length; charIndex += 1) {
const char = item.charCodeAt(charIndex);
hash = (hash << 5) + hash + char; /* hash * 33 + c */
}
return hash % this.size;
return Math.abs(hash % this.size);
}
/**
* @param {string} str
* @param {string} item
* @return {number}
*/
hash3(str) {
hash3(item) {
let hash = 0;
for (let i = 0; i < str.length; i += 1) {
const char = str.charCodeAt(i);
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 hash % this.size;
return Math.abs(hash % this.size);
}
/**
* Runs all 3 hash functions on the input and returns an array of results
* Runs all 3 hash functions on the input and returns an array of results.
*
* @param {string} str
* @param {string} item
* @return {number[]}
*/
getHashValues(item) {
return [this.hash1(item), Math.abs(this.hash2(item)), Math.abs(this.hash3(item))];
return [
this.hash1(item),
this.hash2(item),
this.hash3(item),
];
}
}

View File

@ -1,39 +1,62 @@
import BloomFilter from '../BloomFilter';
describe('Bloom Filter', () => {
describe('BloomFilter', () => {
let bloomFilter;
const people = ['Bruce Wayne', 'Clark Kent', 'Barry Allen'];
const people = [
'Bruce Wayne',
'Clark Kent',
'Barry Allen',
];
beforeEach(() => {
bloomFilter = new BloomFilter();
});
it('Should have methods named "insert" and "mayContain"', () => {
it('should have methods named "insert" and "mayContain"', () => {
expect(typeof bloomFilter.insert).toBe('function');
expect(typeof bloomFilter.mayContain).toBe('function');
});
it('Should create a new filter store with the appropriate methods', () => {
it('should create a new filter store with the appropriate methods', () => {
const store = bloomFilter.createStore(18);
expect(typeof store.getValue).toBe('function');
expect(typeof store.setValue).toBe('function');
});
it('Should hash deterministically with all 3 hash functions', () => {
const str = 'abc';
expect(bloomFilter.hash1(str)).toEqual(bloomFilter.hash1(str));
expect(bloomFilter.hash2(str)).toEqual(bloomFilter.hash2(str));
expect(bloomFilter.hash3(str)).toEqual(bloomFilter.hash3(str));
it('should hash deterministically with all 3 hash functions', () => {
const str1 = 'apple';
expect(bloomFilter.hash1(str1)).toEqual(bloomFilter.hash1(str1));
expect(bloomFilter.hash2(str1)).toEqual(bloomFilter.hash2(str1));
expect(bloomFilter.hash3(str1)).toEqual(bloomFilter.hash3(str1));
expect(bloomFilter.hash1(str1)).toBe(14);
expect(bloomFilter.hash2(str1)).toBe(43);
expect(bloomFilter.hash3(str1)).toBe(10);
const str2 = 'orange';
expect(bloomFilter.hash1(str2)).toEqual(bloomFilter.hash1(str2));
expect(bloomFilter.hash2(str2)).toEqual(bloomFilter.hash2(str2));
expect(bloomFilter.hash3(str2)).toEqual(bloomFilter.hash3(str2));
expect(bloomFilter.hash1(str2)).toBe(0);
expect(bloomFilter.hash2(str2)).toBe(61);
expect(bloomFilter.hash3(str2)).toBe(10);
});
it('Should create an array with 3 hash values', () => {
expect(bloomFilter.getHashValues('abc').length).toEqual(3);
it('should create an array with 3 hash values', () => {
expect(bloomFilter.getHashValues('abc').length).toBe(3);
expect(bloomFilter.getHashValues('abc')).toEqual([66, 63, 54]);
});
it('Should insert strings correctly and return true when checking for inserted values', () => {
it('should insert strings correctly and return true when checking for inserted values', () => {
people.forEach(person => bloomFilter.insert(person));
expect(bloomFilter.mayContain('Bruce Wayne')).toBe(true);
expect(bloomFilter.mayContain('Clark Kent')).toBe(true);
expect(bloomFilter.mayContain('Barry Allen')).toBe(true);
expect(bloomFilter.mayContain('Bruce Wayne')).toBeTruthy();
expect(bloomFilter.mayContain('Clark Kent')).toBeTruthy();
expect(bloomFilter.mayContain('Barry Allen')).toBeTruthy();
expect(bloomFilter.mayContain('Tony Stark')).toBeFalsy();
});
});