Last active
October 25, 2021 14:06
-
-
Save hathix/19596d3ae0ca045f06ba2dcf81bb06a1 to your computer and use it in GitHub Desktop.
TypeScript immutable map
This file contains 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 ImmutableMap<T> { | |
// This is immutable, can't be changed in place | |
private innerMap: Map<string,T>; | |
public constructor(nativeMap = new Map<string,T>()) { | |
this.innerMap = nativeMap; | |
} | |
public keys() : string[] { | |
return Array.from(this.innerMap.keys()); | |
} | |
public values() : T[] { | |
return Array.from(this.innerMap.values()); | |
} | |
public entries(): [string,T][] { | |
return Array.from(this.innerMap.entries()); | |
} | |
public get(key: string) : T|undefined { | |
return this.innerMap.get(key); | |
} | |
// Does not edit in place, returns a new one | |
public set(key: string, value: T) : ImmutableMap<T> { | |
// Create a copy | |
const newMap: Map<string,T> = new Map<string,T>(this.innerMap); | |
// Add to it | |
newMap.set(key, value); | |
// Return the new one | |
return new ImmutableMap<T>(newMap); | |
} | |
// Does not edit in place | |
public remove(key: string) : ImmutableMap<T> { | |
// Create a copy | |
const newMap: Map<string,T> = new Map<string,T>(this.innerMap); | |
// Remove from it | |
newMap.delete(key); | |
// Return the new one | |
return new ImmutableMap<T>(newMap); | |
} | |
public mapToArray<U>(fn: (key: string, value: T) => U) : U[] { | |
return this.entries().map(([key, value]: [string, T]) => { | |
return fn(key, value); | |
}); | |
} | |
// Semantic alternative for mapToArray() | |
public forEach(fn: (key: string, value: T) => void) : void { | |
this.mapToArray<void>(fn); | |
} | |
public mapToMap<U>(fn: (key: string, value: T) => [string, U]) : ImmutableMap<U> { | |
// Create a new empty map and populate it with each new element | |
const newMap: Map<string, U> = new Map<string, U>(); | |
this.entries().forEach(([key, value]: [string, T]) => { | |
// Transform it | |
const [newKey, newValue] = fn(key, value); | |
// Set it | |
newMap.set(newKey, newValue); | |
}); | |
return new ImmutableMap<U>(newMap); | |
} | |
public filter(predicate: (key: string, value: T) => boolean) : ImmutableMap<T> { | |
const allowedKeys: string[] = this.keys().filter((key: string) => { | |
const value: T = this.innerMap.get(key)!; | |
return predicate(key, value); | |
}); | |
// Build up a new map with these keys | |
const newMap: Map<string, T> = new Map<string, T>(); | |
allowedKeys.forEach((key: string) => { | |
newMap.set(key, this.innerMap.get(key)!); | |
}); | |
return new ImmutableMap<T>(newMap); | |
} | |
public toString() : string { | |
return this.entries().map(([key, value]: [string, T]) => { | |
return `${key} => ${value}`; | |
}).join("\n"); | |
} | |
public print() : void { | |
console.log(this.toString()); | |
} | |
} | |
// Testing | |
let m = new ImmutableMap<number>(); | |
m = m.set("A", 1); | |
m = m.set("B", 2); | |
m = m.set("C", 3); | |
m.set("D", 4); // Shouldn't show up | |
// A => 1, B => 2, C => 3 | |
console.log(m.toString()); | |
// B => 2, C => 3 | |
console.log(m.filter((k, v) => v >= 2).toString()); | |
// B! => 12, C! => 13 | |
console.log(m.filter((k, v) => v >= 2).mapToMap((k, v) => [k+"!", v+10]).toString()); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment