Skip to content

Commit a5da61f

Browse files
committed
Add hash table
1 parent ddc7510 commit a5da61f

File tree

2 files changed

+165
-0
lines changed

2 files changed

+165
-0
lines changed
+82
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import { HashTable } from './hash-table';
2+
3+
describe('HashTable', () => {
4+
it('should create an instance', () => {
5+
const hashTable = new HashTable<string, number>();
6+
expect(hashTable).toBeInstanceOf(HashTable);
7+
expect(hashTable.size()).toBe(0);
8+
expect(hashTable.keys()).toEqual([]);
9+
expect(hashTable.values()).toEqual([]);
10+
});
11+
12+
it('should set and get values', () => {
13+
const hashTable = new HashTable<string, number>();
14+
hashTable.set('one', 1);
15+
expect(hashTable.size()).toBe(1);
16+
expect(hashTable.get('one')).toBe(1);
17+
hashTable.set('two', 2);
18+
expect(hashTable.size()).toBe(2);
19+
expect(hashTable.get('two')).toBe(2);
20+
hashTable.set('one', 10);
21+
expect(hashTable.size()).toBe(2);
22+
expect(hashTable.get('one')).toBe(10);
23+
});
24+
25+
it('should remove values', () => {
26+
const hashTable = new HashTable<string, number>();
27+
hashTable.set('one', 1);
28+
hashTable.set('two', 2);
29+
expect(hashTable.size()).toBe(2);
30+
hashTable.remove('one');
31+
expect(hashTable.size()).toBe(1);
32+
expect(hashTable.get('one')).toBeUndefined();
33+
expect(hashTable.get('two')).toBe(2);
34+
});
35+
36+
it('should check if key exists', () => {
37+
const hashTable = new HashTable<string, number>();
38+
hashTable.set('one', 1);
39+
expect(hashTable.has('one')).toBe(true);
40+
expect(hashTable.has('two')).toBe(false);
41+
});
42+
43+
it('should clear the hash table', () => {
44+
const hashTable = new HashTable<string, number>();
45+
hashTable.set('one', 1);
46+
hashTable.set('two', 2);
47+
expect(hashTable.size()).toBe(2);
48+
hashTable.clear();
49+
expect(hashTable.size()).toBe(0);
50+
expect(hashTable.get('one')).toBeUndefined();
51+
expect(hashTable.get('two')).toBeUndefined();
52+
});
53+
54+
it('should return all keys and values', () => {
55+
const hashTable = new HashTable<string, number>();
56+
hashTable.set('one', 1);
57+
hashTable.set('two', 2);
58+
hashTable.set('three', 3);
59+
expect(hashTable.keys().sort()).toEqual(['one', 'three', 'two']);
60+
expect(hashTable.values().sort()).toEqual([1, 2, 3]);
61+
});
62+
63+
it('should increase the size when adding a new key', () => {
64+
const hashTable = new HashTable<string, number>(1);
65+
hashTable.set('one', 1);
66+
hashTable.set('two', 2);
67+
hashTable.set('three', 3);
68+
expect(hashTable.size()).toBe(3);
69+
expect(hashTable.get('one')).toBe(1);
70+
expect(hashTable.get('two')).toBe(2);
71+
expect(hashTable.get('three')).toBe(3);
72+
});
73+
74+
it('should handle different key types', () => {
75+
const hashTable = new HashTable<number | string, string>();
76+
hashTable.set(1, 'one');
77+
hashTable.set('two', 'two');
78+
expect(hashTable.get(1)).toBe('one');
79+
expect(hashTable.get('two')).toBe('two');
80+
expect(hashTable.size()).toBe(2);
81+
});
82+
});

src/data-structures/hash-table.ts

+83
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
export interface IHashTable<TKey, TValue> {
2+
clear(): void;
3+
size(): number;
4+
keys(): TKey[];
5+
values(): TValue[];
6+
has(key: TKey): boolean;
7+
remove(key: TKey): void;
8+
set(key: TKey, value: TValue): void;
9+
get(key: TKey): TValue | undefined;
10+
}
11+
12+
export class HashTable<TKey, TValue> implements IHashTable<TKey, TValue> {
13+
protected _size: number;
14+
protected _buckets: Array<Array<[TKey, TValue]>>;
15+
16+
constructor(bucketSize = 16) {
17+
this._size = 0;
18+
this._buckets = new Array(bucketSize).fill(null).map(() => []);
19+
}
20+
21+
protected hash(key: TKey): number {
22+
const stringKey = String(key);
23+
24+
let total = 0;
25+
for (let i = 0; i < stringKey.length; i++) {
26+
total += stringKey.charCodeAt(i);
27+
}
28+
29+
return total % this._buckets.length;
30+
}
31+
32+
set(key: TKey, value: TValue): void {
33+
const index = this.hash(key);
34+
const bucket = this._buckets[index];
35+
const item = bucket.find(([k]) => k === key);
36+
37+
if (item) {
38+
item[1] = value;
39+
} else {
40+
bucket.push([key, value]);
41+
this._size++;
42+
}
43+
}
44+
45+
get(key: TKey): TValue | undefined {
46+
const index = this.hash(key);
47+
const item = this._buckets[index].find(([k]) => k === key);
48+
return item ? item[1] : undefined;
49+
}
50+
51+
remove(key: TKey): void {
52+
const index = this.hash(key);
53+
const bucket = this._buckets[index];
54+
const itemIndex = bucket.findIndex(([k]) => k === key);
55+
56+
if (itemIndex !== -1) {
57+
bucket.splice(itemIndex, 1);
58+
this._size--;
59+
}
60+
}
61+
62+
has(key: TKey): boolean {
63+
const index = this.hash(key);
64+
return this._buckets[index].some(([k]) => k === key);
65+
}
66+
67+
clear(): void {
68+
this._buckets = this._buckets.map(() => []);
69+
this._size = 0;
70+
}
71+
72+
size(): number {
73+
return this._size;
74+
}
75+
76+
keys(): TKey[] {
77+
return this._buckets.flatMap((bucket) => bucket.map(([k]) => k));
78+
}
79+
80+
values(): TValue[] {
81+
return this._buckets.flatMap((bucket) => bucket.map(([, v]) => v));
82+
}
83+
}

0 commit comments

Comments
 (0)