// ets_tracing: off import "../../../Operator/index.js" import type { Either } from "../../../Either/index.js" import type { Predicate, Refinement } from "../../../Function/index.js" import * as O from "../../../Option/index.js" import type { MutableRecord } from "../../../Support/Mutable/index.js" import type { PredicateWithIndex, RefinementWithIndex } from "../../../Utils/index.js" import * as A from "../Array/index.js" import * as Tp from "../Tuple/index.js" /* adapted from https://github.com/gcanti/fp-ts */ export type Dictionary = { readonly [P in string]: T } /** * Build a readonly record from a mutable version */ export function fromMutable(r: MutableRecord): Dictionary { return Object.assign({}, r) } /** * Converts the record to a mutable version */ export function toMutable(r: Dictionary): MutableRecord { return Object.assign({}, r) } /** * Calculate the number of key/value pairs in a record */ export function size(r: Dictionary): number { return Object.keys(r).length } /** * Test whether a record is empty */ export function isEmpty(r: Dictionary): boolean { return Object.keys(r).length === 0 } /** * Extract record keys */ export function keys(r: Dictionary): ReadonlyArray { return Object.keys(r).sort() as any } /** * Extract record values */ export function values(r: Dictionary): ReadonlyArray { return Object.keys(r) .sort() .map((s) => r[s]!) } /** * Map a record into an array */ export function collect( f: (k: string, a: A) => B ): (r: Dictionary) => ReadonlyArray { return (r) => collect_(r, f) } /** * Map a record into an array */ export function collect_( r: Dictionary, f: (k: string, a: A) => B ): ReadonlyArray { const out: Array = [] for (const key of keys(r)) { out.push(f(key, r[key]!)) } return out } /** * Insert or replace a key/value pair in a record */ export function insertAt(k: string, a: A): (r: Dictionary) => Dictionary { return (r) => insertAt_(r, k, a) } /** * Insert or replace a key/value pair in a record */ export function insertAt_(r: Dictionary, k: string, a: A): Dictionary { if (r[k] === a) { return r } const out: MutableRecord = Object.assign({}, r) out[k] = a return out } /** * Check if k is a key */ export function hasOwnProperty(r: Dictionary, k: string): boolean { return Object.prototype.hasOwnProperty.call(r, k) } /** * Delete a key */ export function deleteAt(k: string): (r: Dictionary) => Dictionary { return (r: Dictionary) => deleteAt_(r, k) } /** * Delete a key */ export function deleteAt_(r: Dictionary, k: string): Dictionary { if (!Object.prototype.hasOwnProperty.call(r, k)) { return r } const out: MutableRecord = Object.assign({}, r) delete out[k] return out } /** * Update a key value pair */ export function updateAt( k: string, a: A ): (r: Dictionary) => O.Option> { return (r: Dictionary) => updateAt_(r, k, a) } /** * Update a key value pair */ export function updateAt_( r: Dictionary, k: string, a: A ): O.Option> { if (!hasOwnProperty(r, k)) { return O.none } if (r[k] === a) { return O.some(r) } const out: MutableRecord = Object.assign({}, r) out[k] = a return O.some(out) } /** * Modify the value at key k with f */ export function modifyAt( k: string, f: (a: A) => A ): (r: Dictionary) => O.Option> { return (r: Dictionary) => modifyAt_(r, k, f) } /** * Modify the value at key k with f */ export function modifyAt_( r: Dictionary, k: string, f: (a: A) => A ): O.Option> { if (!hasOwnProperty(r, k)) { return O.none } const out: MutableRecord = Object.assign({}, r) out[k] = f(r[k]!) return O.some(out) } /** * Delete a key and value from a map, returning the value as well as the subsequent map */ export function pop( k: string ): (r: Dictionary) => O.Option]>> { return (r) => pop_(r, k) } /** * Delete a key and value from a map, returning the value as well as the subsequent map */ export function pop_( r: Dictionary, k: string ): O.Option]>> { const deleteAtk = deleteAt(k) const oa = lookup_(r, k) return O.isNone(oa) ? O.none : O.some(Tp.tuple(oa.value, deleteAtk(r))) } /** * Lookup the value for a key in a record */ export function lookup_(r: Dictionary, k: string): O.Option { return Object.prototype.hasOwnProperty.call(r, k) ? O.some(r[k]!) : O.none } /** * Lookup the value for a key in a record */ export function lookup(k: string): (r: Dictionary) => O.Option { return (r) => (Object.prototype.hasOwnProperty.call(r, k) ? O.some(r[k]!) : O.none) } /** * Empty record */ export const empty: Dictionary = {} /** * Map a record passing the keys to the iterating function */ export function mapWithIndex( f: (k: string, a: A) => B ): (fa: Dictionary) => Dictionary { return (fa) => mapWithIndex_(fa, f) } /** * Map a record passing the keys to the iterating function */ export function mapWithIndex_( fa: Dictionary, f: (k: string, a: A) => B ): Dictionary { const out: MutableRecord = {} const keys = Object.keys(fa) for (const key of keys) { out[key] = f(key, fa[key]!) } return out } /** * Map a record passing the values to the iterating function */ export function map(f: (a: A) => B): (fa: Dictionary) => Dictionary { return (fa) => map_(fa, f) } /** * Map a record passing the values to the iterating function */ export function map_(fa: Dictionary, f: (a: A) => B): Dictionary { return mapWithIndex_(fa, (_, a) => f(a)) } /** * Reduce the record passing the index toghether with the value to f */ export function reduceWithIndex( b: B, f: (k: string, b: B, a: A) => B ): (fa: Dictionary) => B { return (fa) => reduceWithIndex_(fa, b, f) } /** * Reduce the record passing the index toghether with the value to f */ export function reduceWithIndex_( fa: Dictionary, b: B, f: (k: string, b: B, a: A) => B ): B { let out = b const keys = Object.keys(fa).sort() const len = keys.length for (let i = 0; i < len; i++) { const k = keys[i]! out = f(k, out, fa[k]!) } return out } /** * Reduce the record passing the index toghether with the value to f * * Inverted order */ export function reduceRightWithIndex( b: B, f: (k: string, a: A, b: B) => B ): (fa: Dictionary) => B { return (fa) => reduceRightWithIndex_(fa, b, f) } /** * Reduce the record passing the index toghether with the value to f * * Inverted order */ export function reduceRightWithIndex_( fa: Dictionary, b: B, f: (k: string, a: A, b: B) => B ): B { let out = b const keys = Object.keys(fa).sort() const len = keys.length for (let i = len - 1; i >= 0; i--) { const k = keys[i]! out = f(k, fa[k]!, out) } return out } /** * Create a record with one key/value pair */ export function singleton(k: string, a: A): Dictionary { return { [k]: a } } /** * Partition a record using f that also consumes the entry key */ export function partitionMapWithIndex( f: (key: string, a: A) => Either ): (fa: Dictionary) => Tp.Tuple<[Dictionary, Dictionary]> { return (fa) => partitionMapWithIndex_(fa, f) } /** * Partition a record using f that also consumes the entry key */ export function partitionMapWithIndex_( fa: Dictionary, f: (key: string, a: A) => Either ): Tp.Tuple<[Dictionary, Dictionary]> { const left: MutableRecord = {} const right: MutableRecord = {} const keys = Object.keys(fa) for (const key of keys) { const e = f(key, fa[key]!) switch (e._tag) { case "Left": left[key] = e.left break case "Right": right[key] = e.right break } } return Tp.tuple(left, right) } /** * Partition a record using a predicate that also consumes the entry key */ export function partitionWithIndex( refinementWithIndex: RefinementWithIndex ): (fa: Dictionary) => Tp.Tuple<[Dictionary, Dictionary]> export function partitionWithIndex( predicateWithIndex: PredicateWithIndex ): (fa: Dictionary) => Tp.Tuple<[Dictionary, Dictionary]> export function partitionWithIndex( predicateWithIndex: PredicateWithIndex ): (fa: Dictionary) => Tp.Tuple<[Dictionary, Dictionary]> { return (fa) => partitionWithIndex_(fa, predicateWithIndex) } /** * Partition a record using a predicate that also consumes the entry key */ export function partitionWithIndex_( fa: Dictionary, refinementWithIndex: RefinementWithIndex ): Tp.Tuple<[Dictionary, Dictionary]> export function partitionWithIndex_( fa: Dictionary, predicateWithIndex: PredicateWithIndex ): Tp.Tuple<[Dictionary, Dictionary]> export function partitionWithIndex_( fa: Dictionary, predicateWithIndex: PredicateWithIndex ): Tp.Tuple<[Dictionary, Dictionary]> { const left: MutableRecord = {} const right: MutableRecord = {} const keys = Object.keys(fa) for (const key of keys) { const a = fa[key]! if (predicateWithIndex(key, a)) { right[key] = a } else { left[key] = a } } return Tp.tuple(left, right) } /** * Filter & map the record entries with f that consumes also the entry index */ export function filterMapWithIndex( f: (key: string, a: A) => O.Option ): (fa: Dictionary) => Dictionary export function filterMapWithIndex( f: (key: string, a: A) => O.Option ): (fa: Dictionary) => Dictionary { return (fa) => filterMapWithIndex_(fa, f) } /** * Filter & map the record entries with f that consumes also the entry index */ export function filterMapWithIndex_( fa: Dictionary, f: (key: string, a: A) => O.Option ): Dictionary { const r: MutableRecord = {} const keys = Object.keys(fa) for (const key of keys) { const optionB = f(key, fa[key]!) if (O.isSome(optionB)) { r[key] = optionB.value } } return r } /** * Filter the record entries with f that consumes also the entry index */ export function filterWithIndex( refinementWithIndex: RefinementWithIndex ): (fa: Dictionary) => Dictionary export function filterWithIndex( predicateWithIndex: PredicateWithIndex ): (fa: Dictionary) => Dictionary export function filterWithIndex( predicateWithIndex: PredicateWithIndex ): (fa: Dictionary) => Dictionary { return (fa) => filterWithIndex_(fa, predicateWithIndex) } /** * Filter the record entries with f that consumes also the entry index */ export function filterWithIndex_( fa: Dictionary, refinementWithIndex: RefinementWithIndex ): Dictionary export function filterWithIndex_( fa: Dictionary, predicateWithIndex: PredicateWithIndex ): Dictionary export function filterWithIndex_( fa: Dictionary, predicateWithIndex: PredicateWithIndex ): Dictionary { const out: MutableRecord = {} let changed = false for (const key in fa) { if (Object.prototype.hasOwnProperty.call(fa, key)) { const a = fa[key]! if (predicateWithIndex(key, a)) { out[key] = a } else { changed = true } } } return changed ? out : fa } /** * Checks a predicate against all the record entries */ export function every(predicate: Predicate): (r: Dictionary) => boolean { return (r) => every_(r, predicate) } /** * Checks a predicate against all the record entries */ export function every_(r: Dictionary, predicate: Predicate): boolean { for (const k in r) { if (!predicate(r[k]!)) { return false } } return true } /** * Checks a predicate against some of the record entries */ export function some(predicate: (a: A) => boolean): (r: Dictionary) => boolean { return (r) => some_(r, predicate) } /** * Checks a predicate against some of the record entries */ export function some_(r: Dictionary, predicate: (a: A) => boolean): boolean { for (const k in r) { if (predicate(r[k]!)) { return true } } return false } /** * Drop the None entries */ export const compact = (fa: Dictionary>): Dictionary => { const r: MutableRecord = {} const keys = Object.keys(fa) for (const key of keys) { const optionA = fa[key]! if (O.isSome(optionA)) { r[key] = optionA.value } } return r } /** * Separate the record entries */ export const separate = ( fa: Dictionary> ): Tp.Tuple<[Dictionary, Dictionary]> => { const left: MutableRecord = {} const right: MutableRecord = {} const keys = Object.keys(fa) for (const key of keys) { const e = fa[key]! switch (e._tag) { case "Left": left[key] = e.left break case "Right": right[key] = e.right break } } return Tp.tuple(left, right) } /** * Filter record entries according to a predicate */ export const filter: { (refinement: Refinement): (fa: Dictionary) => Dictionary (predicate: Predicate): (fa: Dictionary) => Dictionary } = (predicate: Predicate) => (fa: Dictionary): Dictionary => filter_(fa, predicate) /** * Filter record entries according to a predicate */ export const filter_: { (fa: Dictionary, refinement: Refinement): Dictionary (fa: Dictionary, predicate: Predicate): Dictionary } = (fa: Dictionary, predicate: Predicate): Dictionary => filterWithIndex_(fa, (_, a) => predicate(a)) /** * Filter & map record entries according to a predicate */ export const filterMap = (f: (a: A) => O.Option) => (fa: Dictionary) => filterMap_(fa, f) /** * Filter & map record entries according to a predicate */ export const filterMap_ = (fa: Dictionary, f: (a: A) => O.Option) => filterMapWithIndex_(fa, (_, a: A) => f(a)) /** * Partition record entries according to a predicate */ export const partition: { (refinement: Refinement): ( fa: Dictionary ) => Tp.Tuple<[Dictionary, Dictionary]> (predicate: Predicate): ( fa: Dictionary ) => Tp.Tuple<[Dictionary, Dictionary]> } = (predicate: Predicate) => (fa: Dictionary): Tp.Tuple<[Dictionary, Dictionary]> => partition_(fa, predicate) /** * Partition record entries according to a predicate */ export const partition_: { (fa: Dictionary, refinement: Refinement): Tp.Tuple< [Dictionary, Dictionary] > (fa: Dictionary, predicate: Predicate): Tp.Tuple< [Dictionary, Dictionary] > } = ( fa: Dictionary, predicate: Predicate ): Tp.Tuple<[Dictionary, Dictionary]> => partitionWithIndex_(fa, (_, a) => predicate(a)) /** * Partition & map record entries */ export const partitionMap: { (f: (a: A) => Either): ( fa: Dictionary ) => Tp.Tuple<[Dictionary, Dictionary]> (f: (a: A) => Either): ( fa: Dictionary ) => Tp.Tuple<[Dictionary, Dictionary]> } = (f: (a: A) => Either) => (fa: Dictionary) => partitionMap_(fa, f) /** * Partition & map record entries */ export const partitionMap_ = (fa: Dictionary, f: (a: A) => Either) => partitionMapWithIndex_(fa, (_, a: A) => f(a)) /** * Reduce record entries */ export const reduce: (b: B, f: (b: B, a: A) => B) => (fa: Dictionary) => B = (b, f) => (fa) => reduce_(fa, b, f) /** * Reduce record entries */ export const reduce_: (fa: Dictionary, b: B, f: (b: B, a: A) => B) => B = ( fa, b, f ) => reduceWithIndex_(fa, b, (_, b, a) => f(b, a)) /** * Reduce record entries in inverted order */ export const reduceRight: ( b: B, f: (a: A, b: B) => B ) => (fa: Dictionary) => B = (b, f) => (fa) => reduceRight_(fa, b, f) /** * Reduce record entries in inverted order */ export const reduceRight_: ( fa: Readonly>, b: B, f: (a: A, b: B) => B ) => B = (fa, b, f) => reduceRightWithIndex_(fa, b, (_, a, b) => f(a, b)) /** * Converts a record into an array of [key, value] */ export const toArray: (r: Dictionary) => ReadonlyArray> = collect(Tp.tuple) /** * Converts an array of [key, value] into a record */ export const fromArray = (_: ReadonlyArray>): Dictionary => A.reduce_(_, {} as Dictionary, (b, { tuple: [k, v] }) => Object.assign(b, { [k]: v }) )