Created
May 18, 2025 06:46
-
-
Save williamyang98/e7592a8ddc5cd7472973c8ddf5c57609 to your computer and use it in GitHub Desktop.
Indexed list in typescript
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
export class IndexedList<K extends string | number | symbol, V> { | |
_index: Partial<Record<K, V>>; | |
_list: V[]; | |
_get_key: (v: V) => K; | |
static make_with_id_field<K extends string | number | symbol, V extends { id: K }>() { | |
return new IndexedList((v: V) => v.id); | |
} | |
constructor(get_key: (v: V) => K) { | |
this._index = {}; | |
this._list = []; | |
this._get_key = get_key; | |
} | |
push(v: V): number { | |
const k = this._get_key(v); | |
if (k in this._index) { | |
throw Error(`Key conflict in indexed list: ${k.toString()}`); | |
} | |
const new_length = this._list.push(v); | |
this._index[k] = v; | |
return new_length; | |
} | |
pop(): V | undefined { | |
const v = this._list.pop(); | |
if (v === undefined) return undefined; | |
const k = this._get_key(v); | |
delete this._index[k] | |
return v; | |
} | |
sort(compare_fn?: (a: V, b: V) => number): readonly V[] { | |
return this._list.sort(compare_fn); | |
} | |
reverse(): readonly V[] { | |
return this._list.reverse(); | |
} | |
splice(start: number, delete_count?: number, ...new_values: V[]): V[] { | |
if (delete_count === undefined || delete_count < 0) { | |
delete_count = 0; | |
} | |
// check for key conflicts | |
const delete_values_shallow = this._list.slice(start, start+delete_count); | |
const delete_keys = new Set(delete_values_shallow.map(v => this._get_key(v))); | |
const pending_add_keys = new Set<K>(); | |
for (const v of new_values) { | |
const k = this._get_key(v); | |
if ((k in this._index) && !(k in delete_keys)) { | |
throw Error(`Attempted to splice existing key ${k.toString()} that isn't going to be removed`); | |
} | |
if (k in pending_add_keys) { | |
throw Error(`Duplicate key in spliced values ${k.toString()}`); | |
} | |
pending_add_keys.add(k); | |
} | |
// perform splice | |
const delete_values = this._list.splice(start, delete_count, ...new_values); | |
for (const v of new_values) { | |
const k = this._get_key(v); | |
this._index[k] = v; | |
} | |
return delete_values; | |
} | |
slice(start?: number, end?: number): readonly V[] { | |
return this._list.slice(start, end); | |
} | |
get_key(v: V): K { | |
return this._get_key(v); | |
} | |
get length(): number { | |
return this._list.length; | |
} | |
get list(): readonly V[] { | |
return this._list; | |
} | |
get record(): Readonly<Partial<Record<K,V>>> { | |
return this._index; | |
} | |
*keys(): Iterator<K> { | |
for (const v of this._list) { | |
yield this._get_key(v); | |
} | |
} | |
*values(): Iterator<V> { | |
for (const v of this._list) { | |
yield v; | |
} | |
} | |
*entries(): Iterator<[K,V]> { | |
for (const v of this._list) { | |
const k = this._get_key(v); | |
yield [k,v]; | |
} | |
} | |
has(k: K): boolean { | |
return (k in this._index); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment