Maps
Terms
Load Factor - the amount of data points vs the amount of storage (data.len / storage.capacity)
Key - a value that is consistently hashable and is used to look up data
Value - data associated with a key
Collision - when two or more keys map to the same cell
type map = {key, value}[][]
hash(key) -> number
take that number % capacity to produce index of item
if there is more than one item stored there (from collisions), linear search at that index
LRU Cache
Least recently used items get evicted
Under the hood it's a linked list supported by a hash map for lookup, if you lookup an item it gets moved to the front of the list.
Super handy because it has a fixed capacity.
type Node<T> = {
value: T;
next?: Node<T>;
prev?: Node<T>;
};
class LRUCache<K, V> {
private length: number;
private lookup: Map<K, Node<V>>;
private reverseLookup: Map<Node<V>, K>;
private head?: Node<V>;
private tail?: Node<V>;
constructor(private capacity: number = 10) {
this.length = 0;
this.head = this.tail = undefined;
this.lookup = new Map<K, Node<V>>();
this.reverseLookup = new Map<Node<V>, K>();
}
update(key: K, value: V): void {
let node = this.lookup.get(key);
if (!node) {
node = createNode(value);
this.length++;
this.prepend(node);
this.trimCache();
this.lookup.set(key, node);
this.reverseLookup.set(node, key);
} else {
this.detach(node);
this.prepend(node);
node.value = value;
}
}
get(key: K): V | undefined {
const node = this.lookup.get(key);
if (!node) return undefined;
this.detach(node);
this.prepend(node);
return node.value;
}
private detach(node: Node<V>): void {
if (node.prev) {
node.prev.next = node.next;
}
if (node.next) {
node.next.prev = node.prev;
}
if (this.head === node) {
this.head = this.head.next;
}
if (this.tail === node) {
this.tail = this.tail.prev;
}
node.next = undefined;
node.prev = undefined;
}
private prepend(node: Node<V>): void {
if (!this.head) {
this.head = this.tail = node;
return;
}
node.next = this.head;
this.head.prev = node;
}
private trimCache(): void {
if (this.length <= this.capacity) {
return;
}
const tail = this.tail!;
this.detach(this.tail);
const key = this.reverseLookup.get(tail) as K;
this.lookup.delete(key);
this.reverseLookup.delete(tail);
this.length--;
}
}
function createNode<T>(value: T): Node<T> {
return { value };
}