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 { export default class BloomFilter {
/** /**
* @param {number} size * @param {number} size - the size of the storage.
*/ */
constructor(size = 100) { constructor(size = 100) {
// Bloom filter size directly affects the likelihood of false positives. // Bloom filter size directly affects the likelihood of false positives.
@ -15,7 +15,7 @@ export default class BloomFilter {
insert(item) { insert(item) {
const hashValues = this.getHashValues(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)); hashValues.forEach(val => this.storage.setValue(val));
} }
@ -26,8 +26,8 @@ export default class BloomFilter {
mayContain(item) { mayContain(item) {
const hashValues = this.getHashValues(item); const hashValues = this.getHashValues(item);
for (let i = 0; i < hashValues.length; i += 1) { for (let hashIndex = 0; hashIndex < hashValues.length; hashIndex += 1) {
if (!this.storage.getValue(hashValues[i])) { if (!this.storage.getValue(hashValues[hashIndex])) {
// We know that the item was definitely not inserted. // We know that the item was definitely not inserted.
return false; return false;
} }
@ -50,7 +50,7 @@ export default class BloomFilter {
const storage = []; const storage = [];
// Initialize all indexes to false // Initialize all indexes to false
for (let i = 0; i < size; i += 1) { for (let storageCellIndex = 0; storageCellIndex < size; storageCellIndex += 1) {
storage.push(false); storage.push(false);
} }
@ -67,14 +67,14 @@ export default class BloomFilter {
} }
/** /**
* @param {string} str * @param {string} item
* @return {number} * @return {number}
*/ */
hash1(str) { hash1(item) {
let hash = 0; let hash = 0;
for (let i = 0; i < str.length; i += 1) { for (let charIndex = 0; charIndex < item.length; charIndex += 1) {
const char = str.charCodeAt(i); const char = item.charCodeAt(charIndex);
hash = (hash << 5) + hash + char; hash = (hash << 5) + hash + char;
hash &= hash; // Convert to 32bit integer hash &= hash; // Convert to 32bit integer
hash = Math.abs(hash); hash = Math.abs(hash);
@ -84,44 +84,48 @@ export default class BloomFilter {
} }
/** /**
* @param {string} str * @param {string} item
* @return {number} * @return {number}
*/ */
hash2(str) { hash2(item) {
let hash = 5381; let hash = 5381;
for (let i = 0; i < str.length; i += 1) { for (let charIndex = 0; charIndex < item.length; charIndex += 1) {
const char = str.charCodeAt(i); const char = item.charCodeAt(charIndex);
hash = (hash << 5) + hash + char; /* hash * 33 + c */ 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} * @return {number}
*/ */
hash3(str) { hash3(item) {
let hash = 0; let hash = 0;
for (let i = 0; i < str.length; i += 1) { for (let charIndex = 0; charIndex < item.length; charIndex += 1) {
const char = str.charCodeAt(i); const char = item.charCodeAt(charIndex);
hash = (hash << 5) - hash; hash = (hash << 5) - hash;
hash += char; hash += char;
hash &= hash; // Convert to 32bit integer 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[]} * @return {number[]}
*/ */
getHashValues(item) { 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'; import BloomFilter from '../BloomFilter';
describe('Bloom Filter', () => { describe('BloomFilter', () => {
let bloomFilter; let bloomFilter;
const people = ['Bruce Wayne', 'Clark Kent', 'Barry Allen']; const people = [
'Bruce Wayne',
'Clark Kent',
'Barry Allen',
];
beforeEach(() => { beforeEach(() => {
bloomFilter = new BloomFilter(); 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.insert).toBe('function');
expect(typeof bloomFilter.mayContain).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); const store = bloomFilter.createStore(18);
expect(typeof store.getValue).toBe('function'); expect(typeof store.getValue).toBe('function');
expect(typeof store.setValue).toBe('function'); expect(typeof store.setValue).toBe('function');
}); });
it('Should hash deterministically with all 3 hash functions', () => { it('should hash deterministically with all 3 hash functions', () => {
const str = 'abc'; const str1 = 'apple';
expect(bloomFilter.hash1(str)).toEqual(bloomFilter.hash1(str));
expect(bloomFilter.hash2(str)).toEqual(bloomFilter.hash2(str)); expect(bloomFilter.hash1(str1)).toEqual(bloomFilter.hash1(str1));
expect(bloomFilter.hash3(str)).toEqual(bloomFilter.hash3(str)); 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', () => { it('should create an array with 3 hash values', () => {
expect(bloomFilter.getHashValues('abc').length).toEqual(3); 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)); people.forEach(person => bloomFilter.insert(person));
expect(bloomFilter.mayContain('Bruce Wayne')).toBe(true);
expect(bloomFilter.mayContain('Clark Kent')).toBe(true); expect(bloomFilter.mayContain('Bruce Wayne')).toBeTruthy();
expect(bloomFilter.mayContain('Barry Allen')).toBe(true); expect(bloomFilter.mayContain('Clark Kent')).toBeTruthy();
expect(bloomFilter.mayContain('Barry Allen')).toBeTruthy();
expect(bloomFilter.mayContain('Tony Stark')).toBeFalsy();
}); });
}); });