Last active
October 2, 2024 11:13
-
-
Save YannRobert/335a5dc5f2cb42575f352eac5a32dc86 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class User { | |
constructor(public name: string, public age: number, public city: string) {} | |
hashCode(): number { | |
let hash = 17; | |
hash = hash * 31 + this.name.hashCode(); | |
hash = hash * 31 + this.age; | |
hash = hash * 31 + this.city.hashCode(); | |
return hash; | |
} | |
equals(other: any): boolean { | |
if (!(other instanceof User)) return false; | |
return this.name === other.name && | |
this.age === other.age && | |
this.city === other.city; | |
} | |
toString(): string { | |
return `User(${this.name}, ${this.age}, ${this.city})`; | |
} | |
} | |
// Example usage: | |
const userSet = new HashSet<User>(); | |
const user1 = new User("Alice", 30, "New York"); | |
const user2 = new User("Bob", 25, "Los Angeles"); | |
const user3 = new User("Charlie", 35, "Chicago"); | |
const user4 = new User("Alice", 30, "New York"); // Same as user1 | |
console.log(userSet.add(user1)); // Output: true | |
console.log(userSet.add(user2)); // Output: true | |
console.log(userSet.add(user3)); // Output: true | |
console.log(userSet.add(user4)); // Output: false (already exists) | |
console.log(userSet.size()); // Output: 3 | |
console.log(userSet.has(user1)); // Output: true | |
console.log(userSet.has(new User("Alice", 30, "New York"))); // Output: true (equal to user1) | |
userSet.delete(user2); | |
console.log(userSet.size()); // Output: 2 | |
console.log("Users in the set:"); | |
for (const user of userSet) { | |
console.log(user.toString()); | |
} | |
// Convert to array and print | |
console.log("Users as array:", userSet.toArray().map(user => user.toString())); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class HashSet<T> { | |
private map: HashMap<T, boolean>; | |
constructor() { | |
this.map = new HashMap<T, boolean>(); | |
} | |
add(value: T): boolean { | |
if (this.map.has(value)) { | |
return false; // Value already exists | |
} | |
this.map.set(value, true); | |
return true; | |
} | |
delete(value: T): boolean { | |
return this.map.delete(value); | |
} | |
has(value: T): boolean { | |
return this.map.has(value); | |
} | |
clear(): void { | |
this.map.clear(); | |
} | |
size(): number { | |
return this.map.getSize(); | |
} | |
// Implement iterable interface | |
*[Symbol.iterator](): Iterator<T> { | |
for (const entry of this.map) { | |
yield entry[0]; // yield the key (value in the set) | |
} | |
} | |
// Optional: Add a method to convert the set to an array | |
toArray(): T[] { | |
return Array.from(this); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Extend the String prototype with Java-like hashCode and equals methods | |
interface String { | |
hashCode(): number; | |
equals(other: any): boolean; | |
} | |
String.prototype.hashCode = function(): number { | |
let hash = 0; | |
for (let i = 0; i < this.length; i++) { | |
const char = this.charCodeAt(i); | |
hash = ((hash << 5) - hash) + char; | |
hash = hash & hash; // Convert to 32-bit integer | |
} | |
return hash; | |
}; | |
String.prototype.equals = function(other: any): boolean { | |
if (typeof other !== 'string') return false; | |
return this === other; | |
}; | |
type Primitive = string | number | boolean | null | undefined; | |
function isPrimitive(value: any): value is Primitive { | |
return ( | |
typeof value === "string" || | |
typeof value === "number" || | |
typeof value === "boolean" || | |
value === null || | |
value === undefined | |
); | |
} | |
function defaultHashCode(value: any): number { | |
if (typeof value === "string") { | |
return value.hashCode(); | |
} | |
if (typeof value === "number") return value; | |
if (typeof value === "boolean") return value ? 1 : 0; | |
if (value === null) return 0; | |
if (value === undefined) return -1; | |
if (typeof value === "object" && "hashCode" in value && typeof value.hashCode === "function") { | |
return value.hashCode(); | |
} | |
// For objects without a hashCode method, use a simple implementation | |
return JSON.stringify(value).split("").reduce((acc, char) => (acc * 31 + char.charCodeAt(0)) | 0, 0); | |
} | |
function defaultEquals(a: any, b: any): boolean { | |
if (a === b) return true; | |
if (typeof a === "string" && typeof b === "string") return a.equals(b); | |
if (isPrimitive(a) || isPrimitive(b)) return a === b; | |
if (a && typeof a === "object" && "equals" in a && typeof a.equals === "function") { | |
return a.equals(b); | |
} | |
return JSON.stringify(a) === JSON.stringify(b); | |
} | |
class Entry<K, V> { | |
constructor(public key: K, public value: V, public next: Entry<K, V> | null = null) {} | |
} | |
class HashMap<K, V> { | |
private buckets: Array<Entry<K, V> | null>; | |
private size: number; | |
private readonly INITIAL_CAPACITY = 16; | |
private readonly LOAD_FACTOR = 0.75; | |
constructor() { | |
this.buckets = new Array(this.INITIAL_CAPACITY).fill(null); | |
this.size = 0; | |
} | |
private getBucketIndex(key: K): number { | |
const hash = defaultHashCode(key); | |
return Math.abs(hash) % this.buckets.length; | |
} | |
set(key: K, value: V): void { | |
if (this.size >= this.LOAD_FACTOR * this.buckets.length) { | |
this.resize(); | |
} | |
const index = this.getBucketIndex(key); | |
let entry = this.buckets[index]; | |
while (entry !== null) { | |
if (defaultEquals(entry.key, key)) { | |
entry.value = value; | |
return; | |
} | |
entry = entry.next; | |
} | |
const newEntry = new Entry(key, value, this.buckets[index]); | |
this.buckets[index] = newEntry; | |
this.size++; | |
} | |
get(key: K): V | undefined { | |
const index = this.getBucketIndex(key); | |
let entry = this.buckets[index]; | |
while (entry !== null) { | |
if (defaultEquals(entry.key, key)) { | |
return entry.value; | |
} | |
entry = entry.next; | |
} | |
return undefined; | |
} | |
delete(key: K): boolean { | |
const index = this.getBucketIndex(key); | |
let entry = this.buckets[index]; | |
let prev: Entry<K, V> | null = null; | |
while (entry !== null) { | |
if (defaultEquals(entry.key, key)) { | |
if (prev === null) { | |
this.buckets[index] = entry.next; | |
} else { | |
prev.next = entry.next; | |
} | |
this.size--; | |
return true; | |
} | |
prev = entry; | |
entry = entry.next; | |
} | |
return false; | |
} | |
has(key: K): boolean { | |
return this.get(key) !== undefined; | |
} | |
clear(): void { | |
this.buckets = new Array(this.INITIAL_CAPACITY).fill(null); | |
this.size = 0; | |
} | |
getSize(): number { | |
return this.size; | |
} | |
private resize(): void { | |
const oldBuckets = this.buckets; | |
this.buckets = new Array(this.buckets.length * 2).fill(null); | |
this.size = 0; | |
for (const bucket of oldBuckets) { | |
let entry = bucket; | |
while (entry !== null) { | |
this.set(entry.key, entry.value); | |
entry = entry.next; | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment