diff --git a/data_structures/hashing/hash_table.ts b/data_structures/hashing/hash_map.ts similarity index 90% rename from data_structures/hashing/hash_table.ts rename to data_structures/hashing/hash_map.ts index 1b63b6f1..3e976553 100644 --- a/data_structures/hashing/hash_table.ts +++ b/data_structures/hashing/hash_map.ts @@ -1,5 +1,7 @@ +import { Map } from "../map"; + /** - * Represents a hash table. + * Represents a hash map. * Time complexity: * - Set, Get, Delete, Has: O(1) on average, O(n) in the worst case. * - Clear: O(m) where m is the number of buckets. @@ -7,13 +9,13 @@ * * @template K The key type. * @template V The value type. - * @param size The size of the hash table. + * @param size The size of the hash map. * @param buckets The buckets in which to store the key-value pairs. - * @param loadFactor The load factor to determine when to resize the hash table. + * @param loadFactor The load factor to determine when to resize the hash map. */ -export class HashTable { +export class HashMap implements Map { private size!: number; - private buckets!: HashTableEntry[][]; + private buckets!: HashMapEntry[][]; private readonly loadFactor = 0.75; constructor() { @@ -45,7 +47,7 @@ export class HashTable { const bucket = this.buckets[index]; if (bucket.length === 0) { - bucket.push(new HashTableEntry(key, value)); + bucket.push(new HashMapEntry(key, value)); this.size++; return; } @@ -57,7 +59,7 @@ export class HashTable { } } - bucket.push(new HashTableEntry(key, value)); + bucket.push(new HashMapEntry(key, value)); this.size++; } @@ -118,7 +120,7 @@ export class HashTable { } /** - * Clears the hash table. + * Clears the hash map. */ clear(): void { this.size = 0; @@ -162,8 +164,8 @@ export class HashTable { * * @returns The entries. */ - entries(): HashTableEntry[] { - const entries: HashTableEntry[] = []; + entries(): HashMapEntry[] { + const entries: HashMapEntry[] = []; for (const bucket of this.buckets) { for (const entry of bucket) { entries.push(entry); @@ -204,7 +206,7 @@ export class HashTable { } /** - * Resizes the hash table by doubling the amount of buckets. + * Resizes the hash map by doubling the amount of buckets. */ private resize(): void { this.initializeBuckets(this.buckets.length * 2); @@ -224,7 +226,7 @@ export class HashTable { * @param key The key. * @param value The value. */ -class HashTableEntry { +export class HashMapEntry { key: K; value: V; diff --git a/data_structures/hashing/hash_map_set.ts b/data_structures/hashing/hash_map_set.ts new file mode 100644 index 00000000..35bfaf2b --- /dev/null +++ b/data_structures/hashing/hash_map_set.ts @@ -0,0 +1,24 @@ +import { Map } from "../map"; +import { MapSet } from "../map_set"; +import { HashMap } from "./hash_map"; + +/** + * This class is a representation of the Set data structure based on a hash map. + * + * @template K The value type. + * @extends MapSet + */ +export class HashMapSet extends MapSet { + constructor() { + super(); + } + + /** + * Initializes the map used to store the set. + * + * @returns {Map} The map used to store the set. + */ + protected initMap(): Map { + return new HashMap(); + } +} diff --git a/data_structures/hashing/test/hash_map.test.ts b/data_structures/hashing/test/hash_map.test.ts new file mode 100644 index 00000000..acec9732 --- /dev/null +++ b/data_structures/hashing/test/hash_map.test.ts @@ -0,0 +1,91 @@ +import { HashMap } from "../hash_map"; + +describe("Hash Map", () => { + let hashMap: HashMap; + beforeEach(() => { + hashMap = new HashMap(); + }); + + it("should set a value", () => { + hashMap.set("a", 1); + + expect(hashMap.values()).toEqual([1]); + }); + + it("should override a value", () => { + hashMap.set("a", 1); + hashMap.set("a", 2); + + expect(hashMap.values()).toEqual([2]); + }); + + it("should get a value", () => { + hashMap.set("a", 1); + + expect(hashMap.get("a")).toBe(1); + }); + + it("should get null if key does not exist", () => { + expect(hashMap.get("a")).toBeNull(); + }); + + it("should delete a value", () => { + hashMap.set("a", 1); + hashMap.delete("a"); + + expect(hashMap.get("a")).toBeNull(); + }); + + it("should do nothing on delete if key does not exist", () => { + hashMap.delete("a"); + + expect(hashMap.get("a")).toBeNull(); + }); + + it("should return true if key exists", () => { + hashMap.set("a", 1); + + expect(hashMap.has("a")).toBe(true); + }); + + it("should return false if key does not exist", () => { + expect(hashMap.has("a")).toBe(false); + }); + + it("should clear the hash table", () => { + hashMap.set("a", 1); + hashMap.set("b", 2); + hashMap.set("c", 3); + hashMap.clear(); + + expect(hashMap.getSize()).toBe(0); + }); + + it("should return all keys", () => { + hashMap.set("a", 1); + hashMap.set("b", 2); + hashMap.set("c", 3); + + expect(hashMap.keys()).toEqual(["a", "b", "c"]); + }); + + it("should return all values", () => { + hashMap.set("a", 1); + hashMap.set("b", 2); + hashMap.set("c", 3); + + expect(hashMap.values()).toEqual([1, 2, 3]); + }); + + it("should return all key-value pairs", () => { + hashMap.set("a", 1); + hashMap.set("b", 2); + hashMap.set("c", 3); + + expect(hashMap.entries()).toEqual([ + { key: "a", value: 1 }, + { key: "b", value: 2 }, + { key: "c", value: 3 }, + ]); + }); +}); diff --git a/data_structures/hashing/test/hash_table.test.ts b/data_structures/hashing/test/hash_table.test.ts deleted file mode 100644 index d54190a8..00000000 --- a/data_structures/hashing/test/hash_table.test.ts +++ /dev/null @@ -1,91 +0,0 @@ -import { HashTable } from "../hash_table"; - -describe("Hash Table", () => { - let hashTable: HashTable; - beforeEach(() => { - hashTable = new HashTable(); - }); - - it("should set a value", () => { - hashTable.set("a", 1); - - expect(hashTable.values()).toEqual([1]); - }); - - it("should override a value", () => { - hashTable.set("a", 1); - hashTable.set("a", 2); - - expect(hashTable.values()).toEqual([2]); - }); - - it("should get a value", () => { - hashTable.set("a", 1); - - expect(hashTable.get("a")).toBe(1); - }); - - it("should get null if key does not exist", () => { - expect(hashTable.get("a")).toBeNull(); - }); - - it("should delete a value", () => { - hashTable.set("a", 1); - hashTable.delete("a"); - - expect(hashTable.get("a")).toBeNull(); - }); - - it("should do nothing on delete if key does not exist", () => { - hashTable.delete("a"); - - expect(hashTable.get("a")).toBeNull(); - }); - - it("should return true if key exists", () => { - hashTable.set("a", 1); - - expect(hashTable.has("a")).toBe(true); - }); - - it("should return false if key does not exist", () => { - expect(hashTable.has("a")).toBe(false); - }); - - it("should clear the hash table", () => { - hashTable.set("a", 1); - hashTable.set("b", 2); - hashTable.set("c", 3); - hashTable.clear(); - - expect(hashTable.getSize()).toBe(0); - }); - - it("should return all keys", () => { - hashTable.set("a", 1); - hashTable.set("b", 2); - hashTable.set("c", 3); - - expect(hashTable.keys()).toEqual(["a", "b", "c"]); - }); - - it("should return all values", () => { - hashTable.set("a", 1); - hashTable.set("b", 2); - hashTable.set("c", 3); - - expect(hashTable.values()).toEqual([1, 2, 3]); - }); - - it("should return all key-value pairs", () => { - hashTable.set("a", 1); - hashTable.set("b", 2); - hashTable.set("c", 3); - - expect(hashTable.entries()).toEqual([ - { key: "a", value: 1 }, - { key: "b", value: 2 }, - { key: "c", value: 3 }, - ]); - }); -}); diff --git a/data_structures/map.ts b/data_structures/map.ts new file mode 100644 index 00000000..46d3e8ac --- /dev/null +++ b/data_structures/map.ts @@ -0,0 +1,16 @@ +import { HashMapEntry } from "./hashing/hash_map"; + +/** + * This interface is a representation of the Map data structure. + */ +export interface Map { + getSize(): number; + set(key: K, value: V): void; + get(key: K): V | null; + delete(key: K): void; + has(key: K): boolean; + clear(): void; + keys(): K[]; + values(): V[]; + entries(): HashMapEntry[]; +} diff --git a/data_structures/map_set.ts b/data_structures/map_set.ts new file mode 100644 index 00000000..11e8c2f2 --- /dev/null +++ b/data_structures/map_set.ts @@ -0,0 +1,75 @@ +import { Map } from "./map"; +import { Set } from "./set"; + +/** + * This class is a representation of the Set data structure based on a hash map. + * + * @template K The value type. + * @implements Set + * @property {Map} map The map used to store the set. + */ +export abstract class MapSet implements Set { + private map: Map; + + constructor() { + this.map = this.initMap(); + } + + /** + * Initializes the map used to store the set. + */ + protected abstract initMap(): Map; + + /** + * Adds a new element to the set. + * + * @param value The value to add to the set. + */ + add(value: K): void { + this.map.set(value, null); + } + + /** + * Removes an element from the set. + * + * @param value The value to remove from the set. + */ + delete(value: K): void { + this.map.delete(value); + } + + /** + * Checks if the set contains a given value. + * + * @param value The value to check for. + * @returns Whether the set contains the value. + */ + has(value: K): boolean { + return this.map.has(value); + } + + /** + * Removes all elements from the set. + */ + clear(): void { + this.map.clear(); + } + + /** + * Returns an array of all the values in the set. + * + * @returns An array of all the values in the set. + */ + values(): K[] { + return this.map.keys(); + } + + /** + * Returns the number of elements in the set. + * + * @returns The number of elements in the set. + */ + getSize(): number { + return this.map.getSize(); + } +} diff --git a/data_structures/set.ts b/data_structures/set.ts new file mode 100644 index 00000000..3d814d41 --- /dev/null +++ b/data_structures/set.ts @@ -0,0 +1,11 @@ +/** + * This interface is a representation of the Set data structure. + */ +export interface Set { + getSize(): number; + add(value: K): void; + delete(value: K): void; + has(value: K): boolean; + clear(): void; + values(): K[]; +}