export type IndexedType = Record type IndexedObjectType = Record export function findEntry(ob: IndexedObjectType, predicate: (value: T) => unknown): [string, T] | undefined { for (const key in ob) { const value = ob[key] if (predicate(value)) { return [key, value] } } return undefined } export function find(ob: IndexedObjectType, predicate: (value: T) => boolean | undefined): T | undefined { for (const key in ob) { const value = ob[key] if (predicate(value)) { return value } } return undefined } export function filter(ob: IndexedObjectType, predicate: (value: T) => boolean | undefined): IndexedObjectType { const result: IndexedObjectType = {} for (const key in ob) { const value = ob[key] if (predicate(value)) { result[key] = value } } return result } export function isEmpty(ob: IndexedObjectType): boolean { return size(ob) === 0 } function objectEntries(ob: IndexedObjectType): [string, T][] { const entries: [string, T][] = [] for (const key in ob) { const value = ob[key] entries.push([key, value]) } return entries } export function sortValues(ob: IndexedObjectType, compare: (a: T, b: T) => number): IndexedObjectType { const entries = objectEntries(ob) entries.sort((a, b) => compare(a[1], b[1])) return create(entries) } export function iterable(ob: IndexedObjectType): Iterable<[string, T]> { return { [Symbol.iterator]: (): Iterator<[string, T]> => { let i = 0 const keys = Object.keys(ob) return { next: (): IteratorResult<[string, T], undefined> => { const entry: [string, T] = [keys[i], ob[keys[i]]] i++ if (i <= keys.length) { return { value: entry, done: false, } } else { return { value: undefined, done: true, } } }, } }, } } export function values(ob: IndexedObjectType): Iterable { return { [Symbol.iterator]: (): Iterator => { let i = 0 const keys = Object.keys(ob) return { next: (): IteratorResult => { const value = ob[keys[i]] i++ if (i <= keys.length) { return { value, done: false, } } else { return { value: undefined, done: true, } } }, } }, } } export function remove(ob: IndexedObjectType, key: string): void { delete ob[key] } export function create(): IndexedObjectType export function create(entries: [string, T][]): IndexedObjectType export function create(source: IndexedObjectType): IndexedObjectType export function create(entries?: [string, T][] | IndexedObjectType): IndexedObjectType { if (!entries) { return {} } else if (Array.isArray(entries)) { const result: IndexedObjectType = {} for (const entry of entries) { result[entry[0]] = entry[1] } return result } else if (typeof entries === 'object') { const result: IndexedObjectType = {} for (const name in entries) { result[name] = entries[name] } return result } else { throw new Error(`Unsupported argument to create: ${typeof entries}`) } } export function set(ob: IndexedObjectType, key: string, value: V): void { ob[key] = value } export function get(ob: IndexedObjectType, key: string): T | undefined { return ob[key] } export function has(ob: IndexedObjectType, key: string): boolean { return Object.keys(ob).indexOf(key) !== -1 } export function allKeys(ob: IndexedObjectType): string[] { const result: string[] = [] for (const key in ob) { result.push(key) } return result } export function allValues(ob: IndexedObjectType): T[] { const result: T[] = [] for (const key in ob) { result.push(ob[key]) } return result } export function size(ob: IndexedObjectType): number { return Object.keys(ob).length } export function merge(ob: IndexedObjectType, other: IndexedObjectType): IndexedObjectType { for (const key in other) { ob[key] = other[key] } return ob } export function undefinedIfEmpty(ob: IndexedObjectType | undefined): IndexedObjectType | undefined { if (!ob) { return undefined } if (isEmpty(ob)) { return undefined } return ob } export function nullIfEmpty(ob: IndexedObjectType | null): IndexedObjectType | null { if (!ob) { return null } if (isEmpty(ob)) { return null } return ob }