export class Collection extends Map { set(key: K, value: V): this { return super.set(key, value) } array(): V[] { return [...this.values()] } first(): V { return this.values().next().value } last(): V { return [...this.values()][this.size - 1] } random(): V { const arr = [...this.values()] return arr[Math.floor(Math.random() * arr.length)] } find(callback: (value: V, key: K) => boolean): V | undefined { for (const key of this.keys()) { const value = this.get(key) as V // eslint-disable-next-line standard/no-callback-literal if (callback(value, key)) return value } } filter(callback: (value: V, key: K) => boolean): Collection { const relevant = new Collection() this.forEach((value, key) => { if (callback(value, key)) relevant.set(key, value) }) return relevant } map(callback: (value: V, key: K) => T): T[] { const results = [] for (const key of this.keys()) { const value = this.get(key) as V results.push(callback(value, key)) } return results } some(callback: (value: V, key: K) => boolean): boolean { for (const key of this.keys()) { const value = this.get(key) as V if (callback(value, key)) return true } return false } every(callback: (value: V, key: K) => boolean): boolean { for (const key of this.keys()) { const value = this.get(key) as V if (!callback(value, key)) return false } return true } reduce( callback: (accumulator: T, value: V, key: K) => T, initialValue?: T, ): T { let accumulator: T = initialValue as T for (const key of this.keys()) { const value = this.get(key) as V accumulator = callback(accumulator, value, key) } return accumulator } static fromObject(object: { [key: string]: V }): Collection { return new Collection(Object.entries(object)) } toObject(): { [name: string]: V } { return Object.fromEntries(this) } }