Skip to content

Commit 63cbf8f

Browse files
authored
structure: set (#111)
* structure: set * fix: hash_set implements isolated set interface * improvement: adds general abstraction * fix: renames & refactors testing * feat: resolves requested changes
1 parent 77334fc commit 63cbf8f

File tree

7 files changed

+231
-103
lines changed

7 files changed

+231
-103
lines changed

data_structures/hashing/hash_table.ts renamed to data_structures/hashing/hash_map.ts

+14-12
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,21 @@
1+
import { Map } from "../map";
2+
13
/**
2-
* Represents a hash table.
4+
* Represents a hash map.
35
* Time complexity:
46
* - Set, Get, Delete, Has: O(1) on average, O(n) in the worst case.
57
* - Clear: O(m) where m is the number of buckets.
68
* - Keys, Values, Entires: O(n + m).
79
*
810
* @template K The key type.
911
* @template V The value type.
10-
* @param size The size of the hash table.
12+
* @param size The size of the hash map.
1113
* @param buckets The buckets in which to store the key-value pairs.
12-
* @param loadFactor The load factor to determine when to resize the hash table.
14+
* @param loadFactor The load factor to determine when to resize the hash map.
1315
*/
14-
export class HashTable<K, V> {
16+
export class HashMap<K, V> implements Map<K, V> {
1517
private size!: number;
16-
private buckets!: HashTableEntry<K, V>[][];
18+
private buckets!: HashMapEntry<K, V>[][];
1719
private readonly loadFactor = 0.75;
1820

1921
constructor() {
@@ -45,7 +47,7 @@ export class HashTable<K, V> {
4547
const bucket = this.buckets[index];
4648

4749
if (bucket.length === 0) {
48-
bucket.push(new HashTableEntry(key, value));
50+
bucket.push(new HashMapEntry(key, value));
4951
this.size++;
5052
return;
5153
}
@@ -57,7 +59,7 @@ export class HashTable<K, V> {
5759
}
5860
}
5961

60-
bucket.push(new HashTableEntry(key, value));
62+
bucket.push(new HashMapEntry(key, value));
6163
this.size++;
6264
}
6365

@@ -118,7 +120,7 @@ export class HashTable<K, V> {
118120
}
119121

120122
/**
121-
* Clears the hash table.
123+
* Clears the hash map.
122124
*/
123125
clear(): void {
124126
this.size = 0;
@@ -162,8 +164,8 @@ export class HashTable<K, V> {
162164
*
163165
* @returns The entries.
164166
*/
165-
entries(): HashTableEntry<K, V>[] {
166-
const entries: HashTableEntry<K, V>[] = [];
167+
entries(): HashMapEntry<K, V>[] {
168+
const entries: HashMapEntry<K, V>[] = [];
167169
for (const bucket of this.buckets) {
168170
for (const entry of bucket) {
169171
entries.push(entry);
@@ -204,7 +206,7 @@ export class HashTable<K, V> {
204206
}
205207

206208
/**
207-
* Resizes the hash table by doubling the amount of buckets.
209+
* Resizes the hash map by doubling the amount of buckets.
208210
*/
209211
private resize(): void {
210212
this.initializeBuckets(this.buckets.length * 2);
@@ -224,7 +226,7 @@ export class HashTable<K, V> {
224226
* @param key The key.
225227
* @param value The value.
226228
*/
227-
class HashTableEntry<K, V> {
229+
export class HashMapEntry<K, V> {
228230
key: K;
229231
value: V;
230232

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { Map } from "../map";
2+
import { MapSet } from "../map_set";
3+
import { HashMap } from "./hash_map";
4+
5+
/**
6+
* This class is a representation of the Set data structure based on a hash map.
7+
*
8+
* @template K The value type.
9+
* @extends MapSet<K>
10+
*/
11+
export class HashMapSet<K> extends MapSet<K> {
12+
constructor() {
13+
super();
14+
}
15+
16+
/**
17+
* Initializes the map used to store the set.
18+
*
19+
* @returns {Map<K, null>} The map used to store the set.
20+
*/
21+
protected initMap(): Map<K, null> {
22+
return new HashMap<K, null>();
23+
}
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import { HashMap } from "../hash_map";
2+
3+
describe("Hash Map", () => {
4+
let hashMap: HashMap<string, number>;
5+
beforeEach(() => {
6+
hashMap = new HashMap();
7+
});
8+
9+
it("should set a value", () => {
10+
hashMap.set("a", 1);
11+
12+
expect(hashMap.values()).toEqual([1]);
13+
});
14+
15+
it("should override a value", () => {
16+
hashMap.set("a", 1);
17+
hashMap.set("a", 2);
18+
19+
expect(hashMap.values()).toEqual([2]);
20+
});
21+
22+
it("should get a value", () => {
23+
hashMap.set("a", 1);
24+
25+
expect(hashMap.get("a")).toBe(1);
26+
});
27+
28+
it("should get null if key does not exist", () => {
29+
expect(hashMap.get("a")).toBeNull();
30+
});
31+
32+
it("should delete a value", () => {
33+
hashMap.set("a", 1);
34+
hashMap.delete("a");
35+
36+
expect(hashMap.get("a")).toBeNull();
37+
});
38+
39+
it("should do nothing on delete if key does not exist", () => {
40+
hashMap.delete("a");
41+
42+
expect(hashMap.get("a")).toBeNull();
43+
});
44+
45+
it("should return true if key exists", () => {
46+
hashMap.set("a", 1);
47+
48+
expect(hashMap.has("a")).toBe(true);
49+
});
50+
51+
it("should return false if key does not exist", () => {
52+
expect(hashMap.has("a")).toBe(false);
53+
});
54+
55+
it("should clear the hash table", () => {
56+
hashMap.set("a", 1);
57+
hashMap.set("b", 2);
58+
hashMap.set("c", 3);
59+
hashMap.clear();
60+
61+
expect(hashMap.getSize()).toBe(0);
62+
});
63+
64+
it("should return all keys", () => {
65+
hashMap.set("a", 1);
66+
hashMap.set("b", 2);
67+
hashMap.set("c", 3);
68+
69+
expect(hashMap.keys()).toEqual(["a", "b", "c"]);
70+
});
71+
72+
it("should return all values", () => {
73+
hashMap.set("a", 1);
74+
hashMap.set("b", 2);
75+
hashMap.set("c", 3);
76+
77+
expect(hashMap.values()).toEqual([1, 2, 3]);
78+
});
79+
80+
it("should return all key-value pairs", () => {
81+
hashMap.set("a", 1);
82+
hashMap.set("b", 2);
83+
hashMap.set("c", 3);
84+
85+
expect(hashMap.entries()).toEqual([
86+
{ key: "a", value: 1 },
87+
{ key: "b", value: 2 },
88+
{ key: "c", value: 3 },
89+
]);
90+
});
91+
});

data_structures/hashing/test/hash_table.test.ts

-91
This file was deleted.

data_structures/map.ts

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { HashMapEntry } from "./hashing/hash_map";
2+
3+
/**
4+
* This interface is a representation of the Map data structure.
5+
*/
6+
export interface Map<K, V> {
7+
getSize(): number;
8+
set(key: K, value: V): void;
9+
get(key: K): V | null;
10+
delete(key: K): void;
11+
has(key: K): boolean;
12+
clear(): void;
13+
keys(): K[];
14+
values(): V[];
15+
entries(): HashMapEntry<K, V>[];
16+
}

data_structures/map_set.ts

+75
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import { Map } from "./map";
2+
import { Set } from "./set";
3+
4+
/**
5+
* This class is a representation of the Set data structure based on a hash map.
6+
*
7+
* @template K The value type.
8+
* @implements Set<K>
9+
* @property {Map<K, null>} map The map used to store the set.
10+
*/
11+
export abstract class MapSet<K> implements Set<K> {
12+
private map: Map<K, null>;
13+
14+
constructor() {
15+
this.map = this.initMap();
16+
}
17+
18+
/**
19+
* Initializes the map used to store the set.
20+
*/
21+
protected abstract initMap(): Map<K, null>;
22+
23+
/**
24+
* Adds a new element to the set.
25+
*
26+
* @param value The value to add to the set.
27+
*/
28+
add(value: K): void {
29+
this.map.set(value, null);
30+
}
31+
32+
/**
33+
* Removes an element from the set.
34+
*
35+
* @param value The value to remove from the set.
36+
*/
37+
delete(value: K): void {
38+
this.map.delete(value);
39+
}
40+
41+
/**
42+
* Checks if the set contains a given value.
43+
*
44+
* @param value The value to check for.
45+
* @returns Whether the set contains the value.
46+
*/
47+
has(value: K): boolean {
48+
return this.map.has(value);
49+
}
50+
51+
/**
52+
* Removes all elements from the set.
53+
*/
54+
clear(): void {
55+
this.map.clear();
56+
}
57+
58+
/**
59+
* Returns an array of all the values in the set.
60+
*
61+
* @returns An array of all the values in the set.
62+
*/
63+
values(): K[] {
64+
return this.map.keys();
65+
}
66+
67+
/**
68+
* Returns the number of elements in the set.
69+
*
70+
* @returns The number of elements in the set.
71+
*/
72+
getSize(): number {
73+
return this.map.getSize();
74+
}
75+
}

data_structures/set.ts

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/**
2+
* This interface is a representation of the Set data structure.
3+
*/
4+
export interface Set<K> {
5+
getSize(): number;
6+
add(value: K): void;
7+
delete(value: K): void;
8+
has(value: K): boolean;
9+
clear(): void;
10+
values(): K[];
11+
}

0 commit comments

Comments
 (0)