diff --git a/docs/modules/ReadonlyRecord.ts.md b/docs/modules/ReadonlyRecord.ts.md index caae504fd..01f7c3556 100644 --- a/docs/modules/ReadonlyRecord.ts.md +++ b/docs/modules/ReadonlyRecord.ts.md @@ -6,6 +6,11 @@ parent: Modules ## ReadonlyRecord overview +The `ReadonlyRecord.ts` module enables dealing in a functional way with +Typescript's `Readonly>` type. That is similar to the +`Record.ts` module, but for a record with all properties +declared as `readonly`. + Added in v2.5.0 --- @@ -108,16 +113,33 @@ Added in v2.5.0 ## compact +Compact a `ReadonlyRecord` of `Option`s discarding the `None` values and +keeping the `Some` values. + **Signature** ```ts export declare const compact: (r: Readonly>>) => Readonly> ``` +**Example** + +```ts +import { compact } from 'fp-ts/ReadonlyRecord' +import { option } from 'fp-ts' + +assert.deepStrictEqual(compact({ a: option.some('foo'), b: option.none, c: option.some('bar') }), { + a: 'foo', + c: 'bar', +}) +``` + Added in v2.5.0 ## separate +Separate a `ReadonlyRecord` of `Either`s into `Left`s and `Right`s. + **Signature** ```ts @@ -126,12 +148,32 @@ export declare const separate: ( ) => Separated>, Readonly>> ``` +**Example** + +```ts +import { separate } from 'fp-ts/ReadonlyRecord' +import { either } from 'fp-ts' + +assert.deepStrictEqual(separate({ a: either.right('foo'), b: either.left('bar'), c: either.right('baz') }), { + right: { + a: 'foo', + c: 'baz', + }, + left: { + b: 'bar', + }, +}) +``` + Added in v2.5.0 # Filterable ## filter +Given a `Predicate`, it produces a new `ReadonlyRecord` keeping only the entries with a +value that satisfies the provided predicate. + **Signature** ```ts @@ -142,10 +184,24 @@ export declare const filter: { } ``` +**Example** + +```ts +import { filter } from 'fp-ts/ReadonlyRecord' + +assert.deepStrictEqual(filter((s: string) => s.length < 4)({ a: 'foo', b: 'bar', c: 'verylong' }), { + a: 'foo', + b: 'bar', +}) +``` + Added in v2.5.0 ## filterMap +Maps a `ReadonlyRecord` with an iterating function that returns an `Option` +and it keeps only the `Some` values discarding the `None`s. + **Signature** ```ts @@ -154,10 +210,25 @@ export declare const filterMap: ( ) => (fa: Readonly>) => Readonly> ``` +**Example** + +```ts +import { filterMap } from 'fp-ts/ReadonlyRecord' +import { option } from 'fp-ts' + +const f = (s: string) => (s.length < 4 ? option.some(`${s} is short`) : option.none) +assert.deepStrictEqual(filterMap(f)({ a: 'foo', b: 'bar', c: 'verylong' }), { + a: 'foo is short', + b: 'bar is short', +}) +``` + Added in v2.5.0 ## partition +Partition a `ReadonlyRecord` into two parts according to a `Predicate`. + **Signature** ```ts @@ -174,10 +245,29 @@ export declare const partition: { } ``` +**Example** + +```ts +import { partition } from 'fp-ts/ReadonlyRecord' + +assert.deepStrictEqual(partition((s: string) => s.length < 4)({ a: 'foo', b: 'bar', c: 'verylong' }), { + left: { + c: 'verylong', + }, + right: { + a: 'foo', + b: 'bar', + }, +}) +``` + Added in v2.5.0 ## partitionMap +Maps a `ReadonlyRecord` with a function returning an `Either` and +partitions the resulting `ReadonlyRecord` into `Left`s and `Right`s. + **Signature** ```ts @@ -186,12 +276,34 @@ export declare const partitionMap: ( ) => (fa: Readonly>) => Separated>, Readonly>> ``` +**Example** + +```ts +import { partitionMap } from 'fp-ts/ReadonlyRecord' +import { either } from 'fp-ts' + +const f = (s: string) => (s.length < 4 ? either.right(`${s} is short`) : either.left(`${s} is not short`)) +assert.deepStrictEqual(partitionMap(f)({ a: 'foo', b: 'bar', c: 'verylong' }), { + left: { + c: 'verylong is not short', + }, + right: { + a: 'foo is short', + b: 'bar is short', + }, +}) +``` + Added in v2.5.0 # Foldable ## foldMap +Map and fold a `ReadonlyRecord`. +Map the `ReadonlyRecord` passing each value to the iterating function. +Then fold the results using the provided `Monoid`. + **Signature** ```ts @@ -201,10 +313,27 @@ export declare function foldMap( export declare function foldMap(M: Monoid): (f: (a: A) => M) => (fa: ReadonlyRecord) => M ``` +**Example** + +```ts +import { foldMap } from 'fp-ts/ReadonlyRecord' +import { Ord } from 'fp-ts/string' +import { Monoid } from 'fp-ts/Monoid' + +const m: Monoid = { empty: '', concat: (x: string, y: string) => (x ? `${x} -> ${y}` : `${y}`) } +const f = (a: number) => `-${a}-` +const x = { c: 3, a: 1, b: 2 } +assert.deepStrictEqual(foldMap(Ord)(m)(f)(x), '-1- -> -2- -> -3-') +``` + Added in v2.5.0 ## reduce +Reduces a `ReadonlyRecord` passing each value to the iterating function. +Entries are processed in order, sorted by key according to +the given `Ord`. + **Signature** ```ts @@ -214,10 +343,24 @@ export declare function reduce( export declare function reduce(b: B, f: (b: B, a: A) => B): (fa: ReadonlyRecord) => B ``` +**Example** + +```ts +import { reduce } from 'fp-ts/ReadonlyRecord' +import { Ord } from 'fp-ts/string' + +const x = { c: 3, a: 'foo', b: false } +assert.deepStrictEqual(reduce(Ord)([] as string[], (b, a) => [...b, `-${a}-`])(x), ['-foo-', '-false-', '-3-']) +``` + Added in v2.5.0 ## reduceRight +Same as `reduce` but entries are processed _from the right_, +i.e. in reverse order, from the last to the first entry, according to +the given `Ord`. + **Signature** ```ts @@ -227,6 +370,16 @@ export declare function reduceRight( export declare function reduceRight(b: B, f: (a: A, b: B) => B): (fa: ReadonlyRecord) => B ``` +**Example** + +```ts +import { reduceRight } from 'fp-ts/ReadonlyRecord' +import { Ord } from 'fp-ts/string' + +const x = { c: 3, a: 'foo', b: false } +assert.deepStrictEqual(reduceRight(Ord)([] as string[], (a, b) => [...b, `-${a}-`])(x), ['-3-', '-false-', '-foo-']) +``` + Added in v2.5.0 # Witherable @@ -265,10 +418,24 @@ export declare function deleteAt( ): (r: ReadonlyRecord) => ReadonlyRecord, A> ``` +**Example** + +```ts +import { deleteAt } from 'fp-ts/ReadonlyRecord' + +assert.deepStrictEqual(deleteAt('a')({ a: 1, b: 2 }), { b: 2 }) +assert.deepStrictEqual(deleteAt('c')({ a: 1, b: 2 }), { a: 1, b: 2 }) +``` + Added in v2.5.0 ## difference +Difference between two `ReadonlyRecord`s. +Takes two `ReadonlyRecord`s and produces a `ReadonlyRecord` composed by the +entries of the two inputs, removing the entries with the same +key in both inputs. + **Signature** ```ts @@ -277,10 +444,23 @@ export declare const difference: ( ) => (first: Readonly>) => Readonly> ``` +**Example** + +```ts +import { difference } from 'fp-ts/ReadonlyRecord' + +assert.deepStrictEqual(difference({ a: 1 })({ a: 1, b: 2 }), { b: 2 }) +assert.deepStrictEqual(difference({ a: 3 })({ a: 1, b: 2 }), { b: 2 }) +assert.deepStrictEqual(difference({ a: 3, c: 3 })({ a: 1, b: 2 }), { b: 2, c: 3 }) +``` + Added in v2.11.0 ## filterMapWithIndex +Maps a `ReadonlyRecord` with an iterating function that takes key and value and +returns an `Option`, keeping only the `Some` values and discarding `None`s. + **Signature** ```ts @@ -289,11 +469,26 @@ export declare function filterMapWithIndex( ): (fa: ReadonlyRecord) => ReadonlyRecord ``` +**Example** + +```ts +import { filterMapWithIndex } from 'fp-ts/ReadonlyRecord' +import { option } from 'fp-ts' + +const f = (key: string, a: number) => (a >= 0 ? option.some(`${key}${a}`) : option.none) +assert.deepStrictEqual(filterMapWithIndex(f)({ a: -1, b: 2, c: 3 }), { + b: 'b2', + c: 'c3', +}) +``` + Added in v2.5.0 ## flap Derivable from `Functor`. +Takes a value and a `ReadonlyRecord` of functions and returns a +`ReadonlyRecord` by applying each function to the input value. **Signature** @@ -301,10 +496,28 @@ Derivable from `Functor`. export declare const flap: (a: A) => (fab: Readonly B>>) => Readonly> ``` +**Example** + +```ts +import { flap } from 'fp-ts/ReadonlyRecord' + +const fab = { x: (n: number) => `${n} times 2`, y: (n: number) => `${n * 2}` } +assert.deepStrictEqual(flap(3)(fab), { + x: '3 times 2', + y: '6', +}) +``` + Added in v2.10.0 ## intersection +Intersection of two `ReadonlyRecord`s. +Takes two `ReadonlyRecord`s and produces a `ReadonlyRecord` combining only the +entries of the two inputswith the same key. +It uses the `concat` function of the provided `Magma` to +combine the elements. + **Signature** ```ts @@ -313,6 +526,18 @@ export declare const intersection: ( ) => (second: Readonly>) => (first: Readonly>) => Readonly> ``` +**Example** + +```ts +import { intersection } from 'fp-ts/ReadonlyRecord' +import { Magma } from 'fp-ts/Magma' + +const m1: Magma = { concat: (x: number, y: number) => x + y } +assert.deepStrictEqual(intersection(m1)({ a: 3, c: 3 })({ a: 1, b: 2 }), { a: 4 }) +const m2: Magma = { concat: (x: number) => x } +assert.deepStrictEqual(intersection(m2)({ a: 3, c: 3 })({ a: 1, b: 2 }), { a: 1 }) +``` + Added in v2.11.0 ## map @@ -325,6 +550,15 @@ Map a `ReadonlyRecord` passing the values to the iterating function. export declare function map(f: (a: A) => B): (fa: ReadonlyRecord) => ReadonlyRecord ``` +**Example** + +```ts +import { map } from 'fp-ts/ReadonlyRecord' + +const f = (n: number) => `-${n}-` +assert.deepStrictEqual(map(f)({ a: 3, b: 5 }), { a: '-3-', b: '-5-' }) +``` + Added in v2.5.0 ## mapWithIndex @@ -339,10 +573,25 @@ export declare function mapWithIndex( ): (fa: ReadonlyRecord) => ReadonlyRecord ``` +**Example** + +```ts +import { mapWithIndex } from 'fp-ts/ReadonlyRecord' + +const f = (k: string, n: number) => `${k.toUpperCase()}-${n}` +assert.deepStrictEqual(mapWithIndex(f)({ a: 3, b: 5 }), { a: 'A-3', b: 'B-5' }) +``` + Added in v2.5.0 ## union +Union of two `ReadonlyRecord`s. +Takes two `ReadonlyRecord`s and produces a `ReadonlyRecord` combining all the +entries of the two inputs. +It uses the `concat` function of the provided `Magma` to +combine the elements with the same key. + **Signature** ```ts @@ -351,6 +600,18 @@ export declare const union: ( ) => (second: Readonly>) => (first: Readonly>) => Readonly> ``` +**Example** + +```ts +import { union } from 'fp-ts/ReadonlyRecord' +import { Magma } from 'fp-ts/Magma' + +const m1: Magma = { concat: (x: number, y: number) => x + y } +assert.deepStrictEqual(union(m1)({ a: 3, c: 3 })({ a: 1, b: 2 }), { a: 4, b: 2, c: 3 }) +const m2: Magma = { concat: (x: number) => x } +assert.deepStrictEqual(union(m2)({ a: 3, c: 3 })({ a: 1, b: 2 }), { a: 1, b: 2, c: 3 }) +``` + Added in v2.11.0 ## upsertAt @@ -363,6 +624,15 @@ Insert or replace a key/value pair in a `ReadonlyRecord`. export declare const upsertAt: (k: string, a: A) => (r: Readonly>) => Readonly> ``` +**Example** + +```ts +import { upsertAt } from 'fp-ts/ReadonlyRecord' + +assert.deepStrictEqual(upsertAt('a', 5)({ a: 1, b: 2 }), { a: 5, b: 2 }) +assert.deepStrictEqual(upsertAt('c', 5)({ a: 1, b: 2 }), { a: 1, b: 2, c: 5 }) +``` + Added in v2.10.0 ## ~~insertAt~~ @@ -389,6 +659,14 @@ Create a `ReadonlyRecord` with one key/value pair. export declare const singleton: (k: string, a: A) => Readonly> ``` +**Example** + +```ts +import { singleton } from 'fp-ts/ReadonlyRecord' + +assert.deepStrictEqual(singleton('a', 1), { a: 1 }) +``` + Added in v2.5.0 # destructors @@ -397,6 +675,11 @@ Added in v2.5.0 Unfolds a `ReadonlyRecord` into a list of key/value pairs. +Given an `Unfoldable` class type `U` such as `array` or `readonlyArray`, +it uses the `unfold` function to create an instance of `U`, +providing an iterating function that iterates over each +key/value pair in the record sorted alphabetically by key. + **Signature** ```ts @@ -408,6 +691,22 @@ export declare function toUnfoldable( ): (r: ReadonlyRecord) => HKT ``` +**Example** + +```ts +import { array, readonlyArray } from 'fp-ts' +import { toUnfoldable } from 'fp-ts/ReadonlyRecord' + +assert.deepStrictEqual(toUnfoldable(array)({ b: 2, a: 1 }), [ + ['a', 1], + ['b', 2], +]) +assert.deepStrictEqual(toUnfoldable(readonlyArray)({ b: 2, a: 1 }), [ + ['a', 1], + ['b', 2], +]) +``` + Added in v2.5.0 # instances @@ -484,26 +783,60 @@ Added in v2.5.0 ## getDifferenceMagma +Produces a `Magma` with a `concat` function that combines +two `ReadonlyRecord`s by making the `difference`. + **Signature** ```ts export declare const getDifferenceMagma: () => Magma>> ``` +**Example** + +```ts +import { getDifferenceMagma, difference, ReadonlyRecord } from 'fp-ts/ReadonlyRecord' +import { Magma } from 'fp-ts/Magma' + +const r1 = { a: 3, c: 3 } +const r2 = { a: 1, b: 2 } +const m: Magma> = getDifferenceMagma() +assert.deepStrictEqual(m.concat(r1, r2), difference(r2)(r1)) +assert.deepStrictEqual(m.concat(r1, r2), { c: 3, b: 2 }) +``` + Added in v2.11.0 ## getEq +Given an `Eq` for the base type, it produces an `Eq` +for a `ReadonlyRecord` of that base type. + **Signature** ```ts export declare function getEq(E: Eq): Eq> ``` +**Example** + +```ts +import { getEq, ReadonlyRecord } from 'fp-ts/ReadonlyRecord' +import { string } from 'fp-ts' +import { Eq } from 'fp-ts/Eq' + +const eq: Eq> = getEq(string.Eq) +assert.deepStrictEqual(eq.equals({ a: 'foo' }, { b: 'bar' }), false) +assert.deepStrictEqual(eq.equals({ a: 'foo' }, { a: 'foo' }), true) +``` + Added in v2.5.0 ## getFoldable +Produces a `Foldable` instance for a `ReadonlyRecord`, using the +provided `Ord` to sort the `ReadonlyRecord`'s entries by key. + **Signature** ```ts @@ -514,6 +847,9 @@ Added in v2.11.0 ## getFoldableWithIndex +Produces a `FoldableWithIndex1` instance for a `ReadonlyRecord`, using the +provided `Ord` to sort the `ReadonlyRecord`'s entries by key. + **Signature** ```ts @@ -524,17 +860,36 @@ Added in v2.11.0 ## getIntersectionSemigroup +Given a `Semigroup` in the base type, it produces a `Semigroup` +in the `ReadonlyRecord` of the base type. +The resulting `Semigroup` concatenates two `ReadonlyRecord`s by +`intersection`. + **Signature** ```ts export declare const getIntersectionSemigroup: (S: Semigroup) => Semigroup>> ``` +**Example** + +```ts +import { getIntersectionSemigroup, ReadonlyRecord } from 'fp-ts/ReadonlyRecord' +import { Semigroup } from 'fp-ts/Semigroup' + +const sNumber: Semigroup = { concat: (x, y) => x - y } +const sReadonlyRecord: Semigroup> = getIntersectionSemigroup(sNumber) +assert.deepStrictEqual(sReadonlyRecord.concat({ a: 1, b: 2 }, { b: 3, c: 4 }), { b: -1 }) +``` + Added in v2.11.0 ## getMonoid -Returns a `Monoid` instance for `ReadonlyRecord`s given a `Semigroup` instance for their values. +Returns a `Monoid` instance for `ReadonlyRecord`s, given a `Semigroup` +instance for the base type. +The `Monoid` makes the union of two `ReadonlyRecord`s comining the +overlapping entries with the provided `Semigroup`. **Signature** @@ -549,13 +904,17 @@ import { SemigroupSum } from 'fp-ts/number' import { getMonoid } from 'fp-ts/ReadonlyRecord' const M = getMonoid(SemigroupSum) -assert.deepStrictEqual(M.concat({ foo: 123 }, { foo: 456 }), { foo: 579 }) +assert.deepStrictEqual(M.concat({ foo: 123, bar: 234 }, { foo: 456, baz: 567 }), { foo: 579, bar: 234, baz: 567 }) ``` Added in v2.5.0 ## getShow +Produces a `Show` for a `ReadonlyRecord`, given a `Show` for the base type +(a `Show` produces a human-readable representation of an instance). +`ReadonlyRecord` entries are sorted by key with the provided `Ord`. + **Signature** ```ts @@ -563,10 +922,25 @@ export declare function getShow(O: Ord): (S: Show) => Show(S: Show): Show> ``` +**Example** + +```ts +import { getShow, ReadonlyRecord } from 'fp-ts/ReadonlyRecord' +import { Show } from 'fp-ts/Show' +import { Ord } from 'fp-ts/string' + +const sNumber: Show = { show: (n: number) => `${n}` } +const sRecord: Show> = getShow(Ord)(sNumber) +assert.deepStrictEqual(sRecord.show({ b: 2, a: 1 }), '{ "a": 1, "b": 2 }') +``` + Added in v2.5.0 ## getTraversable +Produces a `Traversable` instance for a `ReadonlyRecord`, using the +provided `Ord` to sort the `ReadonlyRecord`'s entries by key. + **Signature** ```ts @@ -577,6 +951,9 @@ Added in v2.11.0 ## getTraversableWithIndex +Produces a `TraversableWithIndex` instance for a `ReadonlyRecord`, using the +provided `Ord` to sort the `ReadonlyRecord`'s entries by key. + **Signature** ```ts @@ -587,22 +964,54 @@ Added in v2.11.0 ## getUnionMonoid +Same as `getMonoid`. +Returns a `Monoid` instance for `ReadonlyRecord`s given a `Semigroup` +instance for the base type. +The `Monoid` makes the union of two `ReadonlyRecord`s combining the +entries that have the same key with the provided `Semigroup`. + **Signature** ```ts export declare const getUnionMonoid: (S: Semigroup) => Monoid>> ``` +**Example** + +```ts +import { SemigroupSum } from 'fp-ts/number' +import { getUnionMonoid } from 'fp-ts/ReadonlyRecord' + +const M = getUnionMonoid(SemigroupSum) +assert.deepStrictEqual(M.concat({ foo: 123, bar: 234 }, { foo: 456, baz: 567 }), { foo: 579, bar: 234, baz: 567 }) +``` + Added in v2.11.0 ## getUnionSemigroup +Given a `Semigroup` in the base type, it produces a `Semigroup` +in the `ReadonlyRecord` of the base type. +The resulting `Semigroup` concatenates two `ReadonlyRecord`s by +`union`. + **Signature** ```ts export declare const getUnionSemigroup: (S: Semigroup) => Semigroup>> ``` +**Example** + +```ts +import { getUnionSemigroup, ReadonlyRecord } from 'fp-ts/ReadonlyRecord' +import { Semigroup } from 'fp-ts/Semigroup' + +const sNumber: Semigroup = { concat: (x, y) => x - y } +const sReadonlyRecord: Semigroup> = getUnionSemigroup(sNumber) +assert.deepStrictEqual(sReadonlyRecord.concat({ a: 1, b: 2 }, { b: 3, c: 4 }), { a: 1, b: -1, c: 4 }) +``` + Added in v2.11.0 ## getWitherable @@ -695,22 +1104,48 @@ Added in v2.5.0 ## fromRecord +Builds a `ReadonlyRecord` by copying a `Record`. + **Signature** ```ts export declare const fromRecord: (r: Record) => Readonly> ``` +**Example** + +```ts +import { ReadonlyRecord, fromRecord } from 'fp-ts/ReadonlyRecord' + +const x: Record = { a: 1, b: 2 } +const y: ReadonlyRecord = fromRecord(x) +assert.deepStrictEqual(x, y) +// `y.a = 5` gives compiler error +``` + Added in v2.5.0 ## toRecord +Builds a mutable `Record` from a `ReadonlyRecord`. + **Signature** ```ts export declare const toRecord: (r: Readonly>) => Record ``` +**Example** + +```ts +import { ReadonlyRecord, toRecord } from 'fp-ts/ReadonlyRecord' + +const x: ReadonlyRecord = { a: 1, b: 2 } +const y: Record = toRecord(x) +assert.deepStrictEqual(x, y) +y.a = 5 // it's ok, y is mutable +``` + Added in v2.5.0 # model @@ -748,17 +1183,18 @@ export declare function collect( import { collect } from 'fp-ts/ReadonlyRecord' import { Ord } from 'fp-ts/string' -const x: { readonly a: string; readonly b: boolean } = { a: 'c', b: false } -assert.deepStrictEqual(collect(Ord)((key, val) => ({ key: key, value: val }))(x), [ - { key: 'a', value: 'c' }, - { key: 'b', value: false }, -]) +const f = (k: string, a: A) => `${k.toUpperCase()}-${a}` +const x = { c: 3, a: 'foo', b: false } +assert.deepStrictEqual(collect(Ord)(f)(x), ['A-foo', 'B-false', 'C-3']) ``` Added in v2.5.0 ## elem +Given an `Eq` checks if a `ReadonlyRecord` contains an entry with +value equal to a provided value. + **Signature** ```ts @@ -770,6 +1206,16 @@ export declare function elem( } ``` +**Example** + +```ts +import { elem } from 'fp-ts/ReadonlyRecord' +import { number } from 'fp-ts' + +assert.deepStrictEqual(elem(number.Eq)(123, { foo: 123, bar: 234 }), true) +assert.deepStrictEqual(elem(number.Eq)(-7, { foo: 123, bar: 234 }), false) +``` + Added in v2.5.0 ## empty @@ -784,16 +1230,30 @@ Added in v2.5.0 ## every +Test if every value in a `ReadonlyRecord` satisfies the predicate. + **Signature** ```ts export declare function every(predicate: Predicate): (r: ReadonlyRecord) => boolean ``` +**Example** + +```ts +import { every } from 'fp-ts/ReadonlyRecord' + +assert.deepStrictEqual(every((n: number) => n >= 0)({ a: 1, b: 2 }), true) +assert.deepStrictEqual(every((n: number) => n >= 0)({ a: 1, b: -1 }), false) +``` + Added in v2.5.0 ## filterWithIndex +Produce a new `ReadonlyRecord` keeping only the entries that satisfy +a predicate taking key and value as input. + **Signature** ```ts @@ -808,10 +1268,24 @@ export declare function filterWithIndex( ): (fa: ReadonlyRecord) => ReadonlyRecord ``` +**Example** + +```ts +import { filterWithIndex } from 'fp-ts/ReadonlyRecord' + +assert.deepStrictEqual(filterWithIndex((s: string, v: number) => s.length <= 1 && v > 0)({ a: 1, b: -2, ccc: 3 }), { + a: 1, +}) +``` + Added in v2.5.0 ## foldMapWithIndex +Map and fold a `ReadonlyRecord`. +Map the `ReadonlyRecord` passing each key/value pair to the iterating function. +Then fold the results using the provided `Monoid`. + **Signature** ```ts @@ -823,6 +1297,19 @@ export declare function foldMapWithIndex( ): (f: (k: K, a: A) => M) => (fa: ReadonlyRecord) => M ``` +**Example** + +```ts +import { foldMapWithIndex } from 'fp-ts/ReadonlyRecord' +import { Ord } from 'fp-ts/string' +import { Monoid } from 'fp-ts/Monoid' + +const m: Monoid = { empty: '', concat: (x: string, y: string) => (x ? `${x} -> ${y}` : `${y}`) } +const f = (k: string, a: number) => `${k}-${a}` +const x = { c: 3, a: 1, b: 2 } +assert.deepStrictEqual(foldMapWithIndex(Ord)(m)(f)(x), 'a-1 -> b-2 -> c-3') +``` + Added in v2.5.0 ## fromFoldable @@ -930,6 +1417,15 @@ Note. This function is not pipeable because is a `Refinement`. export declare const has: (k: string, r: Readonly>) => k is K ``` +**Example** + +```ts +import { has } from 'fp-ts/ReadonlyRecord' + +assert.deepStrictEqual(has('a', { a: 1, b: 2 }), true) +assert.deepStrictEqual(has('c', { a: 1, b: 2 }), false) +``` + Added in v2.10.0 ## isEmpty @@ -942,11 +1438,21 @@ Test whether a `ReadonlyRecord` is empty. export declare const isEmpty: (r: Readonly>) => boolean ``` +**Example** + +```ts +import { isEmpty } from 'fp-ts/ReadonlyRecord' + +assert.deepStrictEqual(isEmpty({}), true) +assert.deepStrictEqual(isEmpty({ a: 3 }), false) +``` + Added in v2.5.0 ## isSubrecord -Test whether one `ReadonlyRecord` contains all of the keys and values contained in another `ReadonlyRecord`. +Test whether one `ReadonlyRecord` contains all of the keys and values +contained in another `ReadonlyRecord`. **Signature** @@ -959,6 +1465,21 @@ export declare function isSubrecord( } ``` +**Example** + +```ts +import { isSubrecord } from 'fp-ts/ReadonlyRecord' +import { string } from 'fp-ts' + +assert.deepStrictEqual(isSubrecord(string.Eq)({ a: 'foo', b: 'bar', c: 'baz' })({ a: 'foo', b: 'bar', c: 'baz' }), true) +assert.deepStrictEqual(isSubrecord(string.Eq)({ a: 'foo', b: 'bar', c: 'baz' })({ a: 'foo', c: 'baz' }), true) +assert.deepStrictEqual( + isSubrecord(string.Eq)({ a: 'foo', b: 'bar', c: 'baz' })({ a: 'foo', b: 'not-bar', c: 'baz' }), + false +) +assert.deepStrictEqual(isSubrecord(string.Eq)({ a: 'foo', b: 'bar' })({ a: 'foo', b: 'bar', c: 'baz' }), false) +``` + Added in v2.5.0 ## keys @@ -982,10 +1503,22 @@ export declare function lookup(k: string): (r: ReadonlyRecord) => export declare function lookup(k: string, r: ReadonlyRecord): Option ``` +**Example** + +```ts +import { lookup } from 'fp-ts/ReadonlyRecord' +import { option } from 'fp-ts' + +assert.deepStrictEqual(lookup('b')({ a: 'foo', b: 'bar' }), option.some('bar')) +assert.deepStrictEqual(lookup('c')({ a: 'foo', b: 'bar' }), option.none) +``` + Added in v2.5.0 ## modifyAt +Applies a mapping function to one spcific key/value pair in a `ReadonlyRecord`. + **Signature** ```ts @@ -995,10 +1528,23 @@ export declare const modifyAt: ( ) => (r: Readonly>) => Option>> ``` +**Example** + +```ts +import { modifyAt } from 'fp-ts/ReadonlyRecord' +import { option } from 'fp-ts' + +assert.deepStrictEqual(modifyAt('a', (x: number) => x * 3)({ a: 1, b: 2 }), option.some({ a: 3, b: 2 })) +assert.deepStrictEqual(modifyAt('c', (x: number) => x * 3)({ a: 1, b: 2 }), option.none) +``` + Added in v2.5.0 ## partitionMapWithIndex +Maps a `ReadonlyRecord` with a function returning an `Either` and +partitions the resulting `ReadonlyRecord` into `Left`s and `Right`s. + **Signature** ```ts @@ -1007,10 +1553,32 @@ export declare function partitionMapWithIndex( ): (fa: ReadonlyRecord) => Separated, ReadonlyRecord> ``` +**Example** + +```ts +import { partitionMapWithIndex } from 'fp-ts/ReadonlyRecord' +import { either } from 'fp-ts' + +const f = (key: string, a: number) => + a >= 0 ? either.right(`${key} is >= 0 (${a})`) : either.left(`${key} is < 0 (${a})`) +assert.deepStrictEqual(partitionMapWithIndex(f)({ a: -1, b: 2, c: 123 }), { + left: { + a: 'a is < 0 (-1)', + }, + right: { + b: 'b is >= 0 (2)', + c: 'c is >= 0 (123)', + }, +}) +``` + Added in v2.5.0 ## partitionWithIndex +Partition a `ReadonlyRecord` into two parts according to a predicate +that takes a key and a value. + **Signature** ```ts @@ -1025,6 +1593,25 @@ export declare function partitionWithIndex( ): (fa: ReadonlyRecord) => Separated, ReadonlyRecord> ``` +**Example** + +```ts +import { partitionWithIndex } from 'fp-ts/ReadonlyRecord' + +assert.deepStrictEqual( + partitionWithIndex((key: string, a: number) => key.length <= 1 && a > 0)({ a: -1, b: 2, ccc: 7 }), + { + left: { + a: -1, + ccc: 7, + }, + right: { + b: 2, + }, + } +) +``` + Added in v2.5.0 ## pop @@ -1041,10 +1628,24 @@ export declare function pop( ) => Option, A>]> ``` +**Example** + +```ts +import { pop } from 'fp-ts/ReadonlyRecord' +import { option } from 'fp-ts' + +assert.deepStrictEqual(pop('a')({ a: 1, b: 2, c: 3 }), option.some([1, { b: 2, c: 3 }])) +assert.deepStrictEqual(pop('x')({ a: 1, b: 2, c: 3 }), option.none) +``` + Added in v2.5.0 ## reduceRightWithIndex +Same as `reduceWithIndex`, but reduce starting from the right +(i.e. in reverse order, from the last to the first entry according to +the given `Ord`). + **Signature** ```ts @@ -1057,10 +1658,28 @@ export declare function reduceRightWithIndex( ): (fa: ReadonlyRecord) => B ``` +**Example** + +```ts +import { reduceRightWithIndex } from 'fp-ts/ReadonlyRecord' +import { Ord } from 'fp-ts/string' + +const x = { c: 3, a: 'foo', b: false } +assert.deepStrictEqual(reduceRightWithIndex(Ord)([] as string[], (k, a, b) => [...b, `${k}-${a}`])(x), [ + 'c-3', + 'b-false', + 'a-foo', +]) +``` + Added in v2.5.0 ## reduceWithIndex +Reduces a `ReadonlyRecord` passing each key/value pair to the iterating function. +Entries are processed in the order, sorted by key according to +the given `Ord`. + **Signature** ```ts @@ -1073,10 +1692,33 @@ export declare function reduceWithIndex( ): (fa: ReadonlyRecord) => B ``` +**Example** + +```ts +import { reduceWithIndex } from 'fp-ts/ReadonlyRecord' +import { Ord } from 'fp-ts/string' + +const x = { c: 3, a: 'foo', b: false } +assert.deepStrictEqual(reduceWithIndex(Ord)([] as string[], (k, b, a) => [...b, `${k}-${a}`])(x), [ + 'a-foo', + 'b-false', + 'c-3', +]) +``` + Added in v2.5.0 ## sequence +`ReadonlyRecord` sequencing, +i.e., take a `ReadonlyRecord` in which elements are monads +and return a monad of a `ReadonlyRecord` of the base types. +The following example for instance shows sequencing +a `ReadonlyRecord>` +into an `Option>`. + +`sequence` in `ReadonlyRecord` is equivalent to `sequenceS` in `Apply.ts`. + **Signature** ```ts @@ -1100,6 +1742,24 @@ export declare function sequence( ): (ta: ReadonlyRecord>) => HKT> ``` +**Example** + +```ts +import { sequence } from 'fp-ts/ReadonlyRecord' +import { option } from 'fp-ts' +import { sequenceS } from 'fp-ts/Apply' + +assert.deepStrictEqual( + sequence(option.Applicative)({ a: option.some(1), b: option.some(2) }), + option.some({ a: 1, b: 2 }) +) +assert.deepStrictEqual(sequence(option.Applicative)({ a: option.some(1), b: option.none }), option.none) +assert.deepStrictEqual( + sequence(option.Applicative)({ a: option.some(1), b: option.some(2) }), + sequenceS(option.Applicative)({ a: option.some(1), b: option.some(2) }) +) +``` + Added in v2.5.0 ## size @@ -1112,16 +1772,35 @@ Calculate the number of key/value pairs in a `ReadonlyRecord`, export declare const size: (r: Readonly>) => number ``` +**Example** + +```ts +import { size } from 'fp-ts/ReadonlyRecord' + +assert.deepStrictEqual(size({ a: true, b: 2, c: 'three' }), 3) +``` + Added in v2.5.0 ## some +Test if at least one value in a `ReadonlyRecord` satisfies the predicate. + **Signature** ```ts export declare function some(predicate: (a: A) => boolean): (r: ReadonlyRecord) => boolean ``` +**Example** + +```ts +import { some } from 'fp-ts/ReadonlyRecord' + +assert.deepStrictEqual(some((n: number) => n >= 0)({ a: 1, b: -2 }), true) +assert.deepStrictEqual(some((n: number) => n >= 0)({ a: -1, b: -2 }), false) +``` + Added in v2.5.0 ## toReadonlyArray @@ -1134,6 +1813,19 @@ Get a sorted `ReadonlyArray` of the key/value pairs contained in a `ReadonlyReco export declare const toReadonlyArray: (r: Readonly>) => readonly (readonly [K, A])[] ``` +**Example** + +```ts +import { toReadonlyArray } from 'fp-ts/ReadonlyRecord' + +const x = { c: 3, a: 'foo', b: false } +assert.deepStrictEqual(toReadonlyArray(x), [ + ['a', 'foo'], + ['b', false], + ['c', 3], +]) +``` + Added in v2.5.0 ## traverse @@ -1210,6 +1902,8 @@ Added in v2.5.0 ## updateAt +Replace a key/value pair in a `ReadonlyRecord`. + **Signature** ```ts @@ -1219,6 +1913,16 @@ export declare const updateAt: ( ) => (r: Readonly>) => Option>> ``` +**Example** + +```ts +import { updateAt } from 'fp-ts/ReadonlyRecord' +import { option } from 'fp-ts' + +assert.deepStrictEqual(updateAt('a', 3)({ a: 1, b: 2 }), option.some({ a: 3, b: 2 })) +assert.deepStrictEqual(updateAt('c', 3)({ a: 1, b: 2 }), option.none) +``` + Added in v2.5.0 ## ~~hasOwnProperty (function)~~ diff --git a/docs/modules/Record.ts.md b/docs/modules/Record.ts.md index e492749cf..c207c7fd2 100644 --- a/docs/modules/Record.ts.md +++ b/docs/modules/Record.ts.md @@ -6,6 +6,9 @@ parent: Modules ## Record overview +The `Record` module enables dealing with Typescript's `Record` +type in a functional way, basically treating it as a `Functor` in `T`. + Added in v2.0.0 --- @@ -101,16 +104,33 @@ Added in v2.0.0 ## compact +Compact a `Record` of `Option`s discarding the `None` values and +keeping the `Some` values. + **Signature** ```ts export declare const compact: (fa: Record>) => Record ``` +**Example** + +```ts +import { compact } from 'fp-ts/Record' +import { option } from 'fp-ts' + +assert.deepStrictEqual(compact({ a: option.some('foo'), b: option.none, c: option.some('bar') }), { + a: 'foo', + c: 'bar', +}) +``` + Added in v2.0.0 ## separate +Separate a `Record` of `Either`s into `Left`s and `Right`s. + **Signature** ```ts @@ -119,12 +139,32 @@ export declare const separate: ( ) => Separated, Record> ``` +**Example** + +```ts +import { separate } from 'fp-ts/Record' +import { either } from 'fp-ts' + +assert.deepStrictEqual(separate({ a: either.right('foo'), b: either.left('bar'), c: either.right('baz') }), { + right: { + a: 'foo', + c: 'baz', + }, + left: { + b: 'bar', + }, +}) +``` + Added in v2.0.0 # Filterable ## filter +Given a `Predicate`, it produces a new `Record` keeping only the entries with a +value that satisfies the provided predicate. + **Signature** ```ts @@ -135,20 +175,49 @@ export declare const filter: { } ``` +**Example** + +```ts +import { filter } from 'fp-ts/Record' + +assert.deepStrictEqual(filter((s: string) => s.length < 4)({ a: 'foo', b: 'bar', c: 'verylong' }), { + a: 'foo', + b: 'bar', +}) +``` + Added in v2.0.0 ## filterMap +Maps a `Record` with an iterating function that returns an `Option` +and it keeps only the `Some` values discarding the `None`s. + **Signature** ```ts export declare const filterMap: (f: (a: A) => Option) => (fa: Record) => Record ``` +**Example** + +```ts +import { filterMap } from 'fp-ts/Record' +import { option } from 'fp-ts' + +const f = (s: string) => (s.length < 4 ? option.some(`${s} is short`) : option.none) +assert.deepStrictEqual(filterMap(f)({ a: 'foo', b: 'bar', c: 'verylong' }), { + a: 'foo is short', + b: 'bar is short', +}) +``` + Added in v2.0.0 ## partition +Partition a `Record` into two parts according to a `Predicate`. + **Signature** ```ts @@ -161,10 +230,29 @@ export declare const partition: { } ``` +**Example** + +```ts +import { partition } from 'fp-ts/Record' + +assert.deepStrictEqual(partition((s: string) => s.length < 4)({ a: 'foo', b: 'bar', c: 'verylong' }), { + left: { + c: 'verylong', + }, + right: { + a: 'foo', + b: 'bar', + }, +}) +``` + Added in v2.0.0 ## partitionMap +Maps a `Record` with a function returning an `Either` and +partitions the resulting `Record` into `Left`s and `Right`s. + **Signature** ```ts @@ -173,12 +261,34 @@ export declare const partitionMap: ( ) => (fa: Record) => Separated, Record> ``` +**Example** + +```ts +import { partitionMap } from 'fp-ts/Record' +import { either } from 'fp-ts' + +const f = (s: string) => (s.length < 4 ? either.right(`${s} is short`) : either.left(`${s} is not short`)) +assert.deepStrictEqual(partitionMap(f)({ a: 'foo', b: 'bar', c: 'verylong' }), { + left: { + c: 'verylong is not short', + }, + right: { + a: 'foo is short', + b: 'bar is short', + }, +}) +``` + Added in v2.0.0 # Foldable ## foldMap +Map and fold a `Record`. +Map the `Record` passing each value to the iterating function. +Then fold the results using the provided `Monoid`. + **Signature** ```ts @@ -188,10 +298,27 @@ export declare function foldMap( export declare function foldMap(M: Monoid): (f: (a: A) => M) => (fa: Record) => M ``` +**Example** + +```ts +import { foldMap } from 'fp-ts/Record' +import { Ord } from 'fp-ts/string' +import { Monoid } from 'fp-ts/Monoid' + +const m: Monoid = { empty: '', concat: (x: string, y: string) => (x ? `${x} -> ${y}` : `${y}`) } +const f = (a: number) => `-${a}-` +const x = { c: 3, a: 1, b: 2 } +assert.deepStrictEqual(foldMap(Ord)(m)(f)(x), '-1- -> -2- -> -3-') +``` + Added in v2.0.0 ## reduce +Reduces a `Record` passing each value to the iterating function. +Entries are processed in order, sorted by key according to +the given `Ord`. + **Signature** ```ts @@ -199,10 +326,24 @@ export declare function reduce(O: Ord): (b: B, f: (b: B, a: A) => export declare function reduce(b: B, f: (b: B, a: A) => B): (fa: Record) => B ``` +**Example** + +```ts +import { reduce } from 'fp-ts/Record' +import { Ord } from 'fp-ts/string' + +const x = { c: 3, a: 'foo', b: false } +assert.deepStrictEqual(reduce(Ord)([] as string[], (b, a) => [...b, `-${a}-`])(x), ['-foo-', '-false-', '-3-']) +``` + Added in v2.0.0 ## reduceRight +Same as `reduce` but entries are processed _from the right_, +i.e. in reverse order, from the last to the first entry, according to +the given `Ord`. + **Signature** ```ts @@ -210,6 +351,16 @@ export declare function reduceRight(O: Ord): (b: B, f: (a: A, b: B export declare function reduceRight(b: B, f: (a: A, b: B) => B): (fa: Record) => B ``` +**Example** + +```ts +import { reduceRight } from 'fp-ts/Record' +import { Ord } from 'fp-ts/string' + +const x = { c: 3, a: 'foo', b: false } +assert.deepStrictEqual(reduceRight(Ord)([] as string[], (a, b) => [...b, `-${a}-`])(x), ['-3-', '-false-', '-foo-']) +``` + Added in v2.0.0 # Witherable @@ -238,17 +389,34 @@ Added in v2.6.5 ## difference +Difference between two `Record`s. +Takes two `Record`s and produces a `Record` composed by the +entries of the two inputs, removing the entries with the same +key in both inputs. + **Signature** ```ts export declare const difference: (second: Record) => (first: Record) => Record ``` +**Example** + +```ts +import { difference } from 'fp-ts/Record' + +assert.deepStrictEqual(difference({ a: 1 })({ a: 1, b: 2 }), { b: 2 }) +assert.deepStrictEqual(difference({ a: 3 })({ a: 1, b: 2 }), { b: 2 }) +assert.deepStrictEqual(difference({ a: 3, c: 3 })({ a: 1, b: 2 }), { b: 2, c: 3 }) +``` + Added in v2.11.0 ## flap Derivable from `Functor`. +Takes a value and a `Record` of functions and returns a +`Record` by applying each function to the input value. **Signature** @@ -256,10 +424,28 @@ Derivable from `Functor`. export declare const flap: (a: A) => (fab: Record B>) => Record ``` +**Example** + +```ts +import { flap } from 'fp-ts/Record' + +const fab = { x: (n: number) => `${n} times 2`, y: (n: number) => `${n * 2}` } +assert.deepStrictEqual(flap(3)(fab), { + x: '3 times 2', + y: '6', +}) +``` + Added in v2.10.0 ## intersection +Intersection of two `Record`s. +Takes two `Record`s and produces a `Record` combining only the +entries of the two inputswith the same key. +It uses the `concat` function of the provided `Magma` to +combine the elements. + **Signature** ```ts @@ -268,10 +454,28 @@ export declare const intersection: ( ) => (second: Record) => (first: Record) => Record ``` +**Example** + +```ts +import { intersection } from 'fp-ts/Record' +import { Magma } from 'fp-ts/Magma' + +const m1: Magma = { concat: (x: number, y: number) => x + y } +assert.deepStrictEqual(intersection(m1)({ a: 3, c: 3 })({ a: 1, b: 2 }), { a: 4 }) +const m2: Magma = { concat: (x: number) => x } +assert.deepStrictEqual(intersection(m2)({ a: 3, c: 3 })({ a: 1, b: 2 }), { a: 1 }) +``` + Added in v2.11.0 ## union +Union of two `Record`s. +Takes two `Record`s and produces a `Record` combining all the +entries of the two inputs. +It uses the `concat` function of the provided `Magma` to +combine the elements with the same key. + **Signature** ```ts @@ -280,6 +484,18 @@ export declare const union: ( ) => (second: Record) => (first: Record) => Record ``` +**Example** + +```ts +import { union } from 'fp-ts/Record' +import { Magma } from 'fp-ts/Magma' + +const m1: Magma = { concat: (x: number, y: number) => x + y } +assert.deepStrictEqual(union(m1)({ a: 3, c: 3 })({ a: 1, b: 2 }), { a: 4, b: 2, c: 3 }) +const m2: Magma = { concat: (x: number) => x } +assert.deepStrictEqual(union(m2)({ a: 3, c: 3 })({ a: 1, b: 2 }), { a: 1, b: 2, c: 3 }) +``` + Added in v2.11.0 ## upsertAt @@ -292,6 +508,15 @@ Insert or replace a key/value pair in a `Record`. export declare const upsertAt: (k: string, a: A) => (r: Record) => Record ``` +**Example** + +```ts +import { upsertAt } from 'fp-ts/Record' + +assert.deepStrictEqual(upsertAt('a', 5)({ a: 1, b: 2 }), { a: 5, b: 2 }) +assert.deepStrictEqual(upsertAt('c', 5)({ a: 1, b: 2 }), { a: 1, b: 2, c: 5 }) +``` + Added in v2.10.0 # instances @@ -368,26 +593,60 @@ Added in v2.0.0 ## getDifferenceMagma +Produces a `Magma` with a `concat` function that combines +two `Record`s by making the `difference`. + **Signature** ```ts export declare const getDifferenceMagma: () => Magma> ``` +**Example** + +```ts +import { getDifferenceMagma, difference } from 'fp-ts/Record' +import { Magma } from 'fp-ts/Magma' + +const r1 = { a: 3, c: 3 } +const r2 = { a: 1, b: 2 } +const m: Magma> = getDifferenceMagma() +assert.deepStrictEqual(m.concat(r1, r2), difference(r2)(r1)) +assert.deepStrictEqual(m.concat(r1, r2), { c: 3, b: 2 }) +``` + Added in v2.11.0 ## getEq +Given an `Eq` for the base type, it produces an `Eq` +for a `Record` of that base type. + **Signature** ```ts export declare const getEq: (E: Eq) => Eq> ``` +**Example** + +```ts +import { getEq } from 'fp-ts/Record' +import { string } from 'fp-ts' +import { Eq } from 'fp-ts/Eq' + +const eq: Eq> = getEq(string.Eq) +assert.deepStrictEqual(eq.equals({ a: 'foo' }, { b: 'bar' }), false) +assert.deepStrictEqual(eq.equals({ a: 'foo' }, { a: 'foo' }), true) +``` + Added in v2.0.0 ## getFoldable +Produces a `Foldable` instance for a `Record`, using the +provided `Ord` to sort the `Record`'s entries by key. + **Signature** ```ts @@ -398,6 +657,9 @@ Added in v2.11.0 ## getFoldableWithIndex +Produces a `FoldableWithIndex1` instance for a `Record`, using the +provided `Ord` to sort the `Record`'s entries by key. + **Signature** ```ts @@ -408,17 +670,36 @@ Added in v2.11.0 ## getIntersectionSemigroup +Given a `Semigroup` in the base type, it produces a `Semigroup` +in the `Record` of the base type. +The resulting `Semigroup` concatenates two `Record`s by +`intersection`. + **Signature** ```ts export declare const getIntersectionSemigroup: (S: Semigroup) => Semigroup> ``` +**Example** + +```ts +import { getIntersectionSemigroup } from 'fp-ts/Record' +import { Semigroup } from 'fp-ts/Semigroup' + +const sNumber: Semigroup = { concat: (x, y) => x - y } +const sRecord: Semigroup> = getIntersectionSemigroup(sNumber) +assert.deepStrictEqual(sRecord.concat({ a: 1, b: 2 }, { b: 3, c: 4 }), { b: -1 }) +``` + Added in v2.11.0 ## getMonoid -Returns a `Monoid` instance for `Record`s given a `Semigroup` instance for their values. +Returns a `Monoid` instance for `Record`s, given a `Semigroup` +instance for the base type. +The `Monoid` makes the union of two `Record`s comining the +overlapping entries with the provided `Semigroup`. **Signature** @@ -433,13 +714,17 @@ import { SemigroupSum } from 'fp-ts/number' import { getMonoid } from 'fp-ts/Record' const M = getMonoid(SemigroupSum) -assert.deepStrictEqual(M.concat({ foo: 123 }, { foo: 456 }), { foo: 579 }) +assert.deepStrictEqual(M.concat({ foo: 123, bar: 234 }, { foo: 456, baz: 567 }), { foo: 579, bar: 234, baz: 567 }) ``` Added in v2.0.0 ## getShow +Produces a `Show` for a `Record`, given a `Show` for the base type +(a `Show` produces a human-readable representation of an instance). +`Record` entries are sorted by key with the provided `Ord`. + **Signature** ```ts @@ -447,10 +732,25 @@ export declare function getShow(O: Ord): (S: Show) => Show(S: Show): Show> ``` +**Example** + +```ts +import { getShow } from 'fp-ts/Record' +import { Show } from 'fp-ts/Show' +import { Ord } from 'fp-ts/string' + +const sNumber: Show = { show: (n: number) => `${n}` } +const sRecord: Show> = getShow(Ord)(sNumber) +assert.deepStrictEqual(sRecord.show({ b: 2, a: 1 }), '{ "a": 1, "b": 2 }') +``` + Added in v2.0.0 ## getTraversable +Produces a `Traversable` instance for a `Record`, using the +provided `Ord` to sort the `Record`'s entries by key. + **Signature** ```ts @@ -461,6 +761,9 @@ Added in v2.11.0 ## getTraversableWithIndex +Produces a `TraversableWithIndex` instance for a `Record`, using the +provided `Ord` to sort the `Record`'s entries by key. + **Signature** ```ts @@ -471,22 +774,54 @@ Added in v2.11.0 ## getUnionMonoid +Same as `getMonoid`. +Returns a `Monoid` instance for `Record`s given a `Semigroup` +instance for the base type. +The `Monoid` makes the union of two `Record`s combining the +entries that have the same key with the provided `Semigroup`. + **Signature** ```ts export declare const getUnionMonoid: (S: Semigroup) => Monoid> ``` +**Example** + +```ts +import { SemigroupSum } from 'fp-ts/number' +import { getUnionMonoid } from 'fp-ts/Record' + +const M = getUnionMonoid(SemigroupSum) +assert.deepStrictEqual(M.concat({ foo: 123, bar: 234 }, { foo: 456, baz: 567 }), { foo: 579, bar: 234, baz: 567 }) +``` + Added in v2.11.0 ## getUnionSemigroup +Given a `Semigroup` in the base type, it produces a `Semigroup` +in the `Record` of the base type. +The resulting `Semigroup` concatenates two `Record`s by +`union`. + **Signature** ```ts export declare const getUnionSemigroup: (S: Semigroup) => Semigroup> ``` +**Example** + +```ts +import { getUnionSemigroup } from 'fp-ts/Record' +import { Semigroup } from 'fp-ts/Semigroup' + +const sNumber: Semigroup = { concat: (x, y) => x - y } +const sRecord: Semigroup> = getUnionSemigroup(sNumber) +assert.deepStrictEqual(sRecord.concat({ a: 1, b: 2 }, { b: 3, c: 4 }), { a: 1, b: -1, c: 4 }) +``` + Added in v2.11.0 ## getWitherable @@ -580,6 +915,8 @@ Added in v2.0.0 ## collect Map a `Record` into an `Array`. +It passes each key/value pair to the iterating function and collects +the results in an array, sorted alphabetically by the original key. **Signature** @@ -596,11 +933,9 @@ export declare function collect(f: (k: K, a: A) => B): ( import { collect } from 'fp-ts/Record' import { Ord } from 'fp-ts/string' -const x: { readonly a: string; readonly b: boolean } = { a: 'c', b: false } -assert.deepStrictEqual(collect(Ord)((key, val) => ({ key: key, value: val }))(x), [ - { key: 'a', value: 'c' }, - { key: 'b', value: false }, -]) +const f = (k: string, a: A) => `${k.toUpperCase()}-${a}` +const x = { c: 3, a: 'foo', b: false } +assert.deepStrictEqual(collect(Ord)(f)(x), ['A-foo', 'B-false', 'C-3']) ``` Added in v2.0.0 @@ -617,10 +952,22 @@ export declare function deleteAt( ): (r: Record) => Record, A> ``` +**Example** + +```ts +import { deleteAt } from 'fp-ts/Record' + +assert.deepStrictEqual(deleteAt('a')({ a: 1, b: 2 }), { b: 2 }) +assert.deepStrictEqual(deleteAt('c')({ a: 1, b: 2 }), { a: 1, b: 2 }) +``` + Added in v2.0.0 ## elem +Given an `Eq` checks if a `Record` contains an entry with +value equal to a provided value. + **Signature** ```ts @@ -629,20 +976,44 @@ export declare const elem: ( ) => { (a: A): (fa: Record) => boolean; (a: A, fa: Record): boolean } ``` +**Example** + +```ts +import { elem } from 'fp-ts/Record' +import { number } from 'fp-ts' + +assert.deepStrictEqual(elem(number.Eq)(123, { foo: 123, bar: 234 }), true) +assert.deepStrictEqual(elem(number.Eq)(-7, { foo: 123, bar: 234 }), false) +``` + Added in v2.0.0 ## every +Test if every value in a `Record` satisfies the predicate. + **Signature** ```ts export declare const every: (predicate: Predicate) => (r: Record) => boolean ``` +**Example** + +```ts +import { every } from 'fp-ts/Record' + +assert.deepStrictEqual(every((n: number) => n >= 0)({ a: 1, b: 2 }), true) +assert.deepStrictEqual(every((n: number) => n >= 0)({ a: 1, b: -1 }), false) +``` + Added in v2.0.0 ## filterMapWithIndex +Maps a `Record` with an iterating function that takes key and value and +returns an `Option`, keeping only the `Some` values and discarding `None`s. + **Signature** ```ts @@ -651,10 +1022,26 @@ export declare const filterMapWithIndex: ( ) => (fa: Record) => Record ``` +**Example** + +```ts +import { filterMapWithIndex } from 'fp-ts/Record' +import { option } from 'fp-ts' + +const f = (key: string, a: number) => (a >= 0 ? option.some(`${key}${a}`) : option.none) +assert.deepStrictEqual(filterMapWithIndex(f)({ a: -1, b: 2, c: 3 }), { + b: 'b2', + c: 'c3', +}) +``` + Added in v2.0.0 ## filterWithIndex +Produce a new `Record` keeping only the entries that satisfy +a predicate taking key and value as input. + **Signature** ```ts @@ -669,10 +1056,24 @@ export declare function filterWithIndex( ): (fa: Record) => Record ``` +**Example** + +```ts +import { filterWithIndex } from 'fp-ts/Record' + +assert.deepStrictEqual(filterWithIndex((s: string, v: number) => s.length <= 1 && v > 0)({ a: 1, b: -2, ccc: 3 }), { + a: 1, +}) +``` + Added in v2.0.0 ## foldMapWithIndex +Map and fold a `Record`. +Map the `Record` passing each key/value pair to the iterating function. +Then fold the results using the provided `Monoid`. + **Signature** ```ts @@ -684,6 +1085,19 @@ export declare function foldMapWithIndex( ): (f: (k: K, a: A) => M) => (fa: Record) => M ``` +**Example** + +```ts +import { foldMapWithIndex } from 'fp-ts/Record' +import { Ord } from 'fp-ts/string' +import { Monoid } from 'fp-ts/Monoid' + +const m: Monoid = { empty: '', concat: (x: string, y: string) => (x ? `${x} -> ${y}` : `${y}`) } +const f = (k: string, a: number) => `${k}-${a}` +const x = { c: 3, a: 1, b: 2 } +assert.deepStrictEqual(foldMapWithIndex(Ord)(m)(f)(x), 'a-1 -> b-2 -> c-3') +``` + Added in v2.0.0 ## fromFoldable @@ -789,6 +1203,15 @@ Note. This function is not pipeable because is a `Refinement`. export declare const has: (k: string, r: Record) => k is K ``` +**Example** + +```ts +import { has } from 'fp-ts/Record' + +assert.deepStrictEqual(has('a', { a: 1, b: 2 }), true) +assert.deepStrictEqual(has('c', { a: 1, b: 2 }), false) +``` + Added in v2.10.0 ## isEmpty @@ -801,11 +1224,21 @@ Test whether a `Record` is empty. export declare const isEmpty: (r: Record) => boolean ``` +**Example** + +```ts +import { isEmpty } from 'fp-ts/Record' + +assert.deepStrictEqual(isEmpty({}), true) +assert.deepStrictEqual(isEmpty({ a: 3 }), false) +``` + Added in v2.0.0 ## isSubrecord -Test whether one `Record` contains all of the keys and values contained in another `Record`. +Test whether one `Record` contains all of the keys and values +contained in another `Record`. **Signature** @@ -818,16 +1251,41 @@ export declare const isSubrecord: ( } ``` +**Example** + +```ts +import { isSubrecord } from 'fp-ts/Record' +import { string } from 'fp-ts' + +assert.deepStrictEqual(isSubrecord(string.Eq)({ a: 'foo', b: 'bar', c: 'baz' })({ a: 'foo', b: 'bar', c: 'baz' }), true) +assert.deepStrictEqual(isSubrecord(string.Eq)({ a: 'foo', b: 'bar', c: 'baz' })({ a: 'foo', c: 'baz' }), true) +assert.deepStrictEqual( + isSubrecord(string.Eq)({ a: 'foo', b: 'bar', c: 'baz' })({ a: 'foo', b: 'not-bar', c: 'baz' }), + false +) +assert.deepStrictEqual(isSubrecord(string.Eq)({ a: 'foo', b: 'bar' })({ a: 'foo', b: 'bar', c: 'baz' }), false) +``` + Added in v2.0.0 ## keys +The keys of a `Record`, sorted alphabetically. + **Signature** ```ts export declare const keys: (r: Record) => K[] ``` +**Example** + +```ts +import { keys } from 'fp-ts/Record' + +assert.deepStrictEqual(keys({ c: 1, a: 2, b: 3 }), ['a', 'b', 'c']) +``` + Added in v2.0.0 ## lookup @@ -843,6 +1301,16 @@ export declare const lookup: { } ``` +**Example** + +```ts +import { lookup } from 'fp-ts/Record' +import { option } from 'fp-ts' + +assert.deepStrictEqual(lookup('b')({ a: 'foo', b: 'bar' }), option.some('bar')) +assert.deepStrictEqual(lookup('c')({ a: 'foo', b: 'bar' }), option.none) +``` + Added in v2.0.0 ## map @@ -855,11 +1323,20 @@ Map a `Record` passing the values to the iterating function. export declare const map: (f: (a: A) => B) => (fa: Record) => Record ``` +**Example** + +```ts +import { map } from 'fp-ts/Record' + +const f = (n: number) => `-${n}-` +assert.deepStrictEqual(map(f)({ a: 3, b: 5 }), { a: '-3-', b: '-5-' }) +``` + Added in v2.0.0 ## mapWithIndex -Map a `Record` passing the keys to the iterating function. +Map a `Record` passing the key/value pairs to the iterating function. **Signature** @@ -867,10 +1344,21 @@ Map a `Record` passing the keys to the iterating function. export declare const mapWithIndex: (f: (k: K, a: A) => B) => (fa: Record) => Record ``` +**Example** + +```ts +import { mapWithIndex } from 'fp-ts/Record' + +const f = (k: string, n: number) => `${k.toUpperCase()}-${n}` +assert.deepStrictEqual(mapWithIndex(f)({ a: 3, b: 5 }), { a: 'A-3', b: 'B-5' }) +``` + Added in v2.0.0 ## modifyAt +Applies a mapping function to one spcific key/value pair in a `Record`. + **Signature** ```ts @@ -880,10 +1368,23 @@ export declare const modifyAt: ( ) => (r: Record) => Option> ``` +**Example** + +```ts +import { modifyAt } from 'fp-ts/Record' +import { option } from 'fp-ts' + +assert.deepStrictEqual(modifyAt('a', (x: number) => x * 3)({ a: 1, b: 2 }), option.some({ a: 3, b: 2 })) +assert.deepStrictEqual(modifyAt('c', (x: number) => x * 3)({ a: 1, b: 2 }), option.none) +``` + Added in v2.0.0 ## partitionMapWithIndex +Maps a `Record` with a function returning an `Either` and +partitions the resulting `Record` into `Left`s and `Right`s. + **Signature** ```ts @@ -892,10 +1393,32 @@ export declare const partitionMapWithIndex: ( ) => (fa: Record) => Separated, Record> ``` +**Example** + +```ts +import { partitionMapWithIndex } from 'fp-ts/Record' +import { either } from 'fp-ts' + +const f = (key: string, a: number) => + a >= 0 ? either.right(`${key} is >= 0 (${a})`) : either.left(`${key} is < 0 (${a})`) +assert.deepStrictEqual(partitionMapWithIndex(f)({ a: -1, b: 2, c: 123 }), { + left: { + a: 'a is < 0 (-1)', + }, + right: { + b: 'b is >= 0 (2)', + c: 'c is >= 0 (123)', + }, +}) +``` + Added in v2.0.0 ## partitionWithIndex +Partition a `Record` into two parts according to a predicate +that takes a key and a value. + **Signature** ```ts @@ -910,6 +1433,25 @@ export declare function partitionWithIndex( ): (fa: Record) => Separated, Record> ``` +**Example** + +```ts +import { partitionWithIndex } from 'fp-ts/Record' + +assert.deepStrictEqual( + partitionWithIndex((key: string, a: number) => key.length <= 1 && a > 0)({ a: -1, b: 2, ccc: 7 }), + { + left: { + a: -1, + ccc: 7, + }, + right: { + b: 2, + }, + } +) +``` + Added in v2.0.0 ## pop @@ -924,10 +1466,24 @@ export declare function pop( ): (r: Record) => Option<[A, Record, A>]> ``` +**Example** + +```ts +import { pop } from 'fp-ts/Record' +import { option } from 'fp-ts' + +assert.deepStrictEqual(pop('a')({ a: 1, b: 2, c: 3 }), option.some([1, { b: 2, c: 3 }])) +assert.deepStrictEqual(pop('x')({ a: 1, b: 2, c: 3 }), option.none) +``` + Added in v2.0.0 ## reduceRightWithIndex +Same as `reduceWithIndex`, but reduce starting from the right +(i.e. in reverse order, from the last to the first entry according to +the given `Ord`). + **Signature** ```ts @@ -940,10 +1496,28 @@ export declare function reduceRightWithIndex( ): (fa: Record) => B ``` +**Example** + +```ts +import { reduceRightWithIndex } from 'fp-ts/Record' +import { Ord } from 'fp-ts/string' + +const x = { c: 3, a: 'foo', b: false } +assert.deepStrictEqual(reduceRightWithIndex(Ord)([] as string[], (k, a, b) => [...b, `${k}-${a}`])(x), [ + 'c-3', + 'b-false', + 'a-foo', +]) +``` + Added in v2.0.0 ## reduceWithIndex +Reduces a `Record` passing each key/value pair to the iterating function. +Entries are processed in the order, sorted by key according to +the given `Ord`. + **Signature** ```ts @@ -956,10 +1530,33 @@ export declare function reduceWithIndex( ): (fa: Record) => B ``` +**Example** + +```ts +import { reduceWithIndex } from 'fp-ts/Record' +import { Ord } from 'fp-ts/string' + +const x = { c: 3, a: 'foo', b: false } +assert.deepStrictEqual(reduceWithIndex(Ord)([] as string[], (k, b, a) => [...b, `${k}-${a}`])(x), [ + 'a-foo', + 'b-false', + 'c-3', +]) +``` + Added in v2.0.0 ## sequence +`Record` sequencing, +i.e., take a `Record` in which elements are monads +and return a monad of a `Record` of the base types. +The following example for instance shows sequencing +a `Record>` +into an `Option>`. + +`sequence` in `Record` is equivalent to `sequenceS` in `Apply.ts`. + **Signature** ```ts @@ -983,6 +1580,24 @@ export declare function sequence( ): (ta: Record>) => HKT> ``` +**Example** + +```ts +import { sequence } from 'fp-ts/Record' +import { option } from 'fp-ts' +import { sequenceS } from 'fp-ts/Apply' + +assert.deepStrictEqual( + sequence(option.Applicative)({ a: option.some(1), b: option.some(2) }), + option.some({ a: 1, b: 2 }) +) +assert.deepStrictEqual(sequence(option.Applicative)({ a: option.some(1), b: option.none }), option.none) +assert.deepStrictEqual( + sequence(option.Applicative)({ a: option.some(1), b: option.some(2) }), + sequenceS(option.Applicative)({ a: option.some(1), b: option.some(2) }) +) +``` + Added in v2.0.0 ## singleton @@ -995,6 +1610,14 @@ Create a `Record` with one key/value pair. export declare const singleton: (k: string, a: A) => Record ``` +**Example** + +```ts +import { singleton } from 'fp-ts/Record' + +assert.deepStrictEqual(singleton('a', 1), { a: 1 }) +``` + Added in v2.0.0 ## size @@ -1007,21 +1630,41 @@ Calculate the number of key/value pairs in a `Record`. export declare const size: (r: Record) => number ``` +**Example** + +```ts +import { size } from 'fp-ts/Record' + +assert.deepStrictEqual(size({ a: true, b: 2, c: 'three' }), 3) +``` + Added in v2.0.0 ## some +Test if at least one value in a `Record` satisfies the predicate. + **Signature** ```ts export declare const some: (predicate: (a: A) => boolean) => (r: Record) => boolean ``` +**Example** + +```ts +import { some } from 'fp-ts/Record' + +assert.deepStrictEqual(some((n: number) => n >= 0)({ a: 1, b: -2 }), true) +assert.deepStrictEqual(some((n: number) => n >= 0)({ a: -1, b: -2 }), false) +``` + Added in v2.0.0 ## toArray Get a sorted `Array` of the key/value pairs contained in a `Record`. +Sorted alphabetically by key. **Signature** @@ -1029,12 +1672,30 @@ Get a sorted `Array` of the key/value pairs contained in a `Record`. export declare const toArray: (r: Record) => [K, A][] ``` +**Example** + +```ts +import { toArray } from 'fp-ts/Record' + +const x = { c: 3, a: 'foo', b: false } +assert.deepStrictEqual(toArray(x), [ + ['a', 'foo'], + ['b', false], + ['c', 3], +]) +``` + Added in v2.0.0 ## toUnfoldable Unfolds a `Record` into a list of key/value pairs. +Given an `Unfoldable` class type `U` such as `array` or `readonlyArray`, +it uses the `unfold` function to create an instance of `U`, +providing an iterating function that iterates over each +key/value pair in the record sorted alphabetically by key. + **Signature** ```ts @@ -1044,6 +1705,22 @@ export declare function toUnfoldable( export declare function toUnfoldable(U: Unfoldable): (r: Record) => HKT ``` +**Example** + +```ts +import { array, readonlyArray } from 'fp-ts' +import { toUnfoldable } from 'fp-ts/Record' + +assert.deepStrictEqual(toUnfoldable(array)({ b: 2, a: 1 }), [ + ['a', 1], + ['b', 2], +]) +assert.deepStrictEqual(toUnfoldable(readonlyArray)({ b: 2, a: 1 }), [ + ['a', 1], + ['b', 2], +]) +``` + Added in v2.0.0 ## traverse @@ -1106,12 +1783,24 @@ Added in v2.0.0 ## updateAt +Replace a key/value pair in a `Record`. + **Signature** ```ts export declare const updateAt: (k: string, a: A) => (r: Record) => Option> ``` +**Example** + +```ts +import { updateAt } from 'fp-ts/Record' +import { option } from 'fp-ts' + +assert.deepStrictEqual(updateAt('a', 3)({ a: 1, b: 2 }), option.some({ a: 3, b: 2 })) +assert.deepStrictEqual(updateAt('c', 3)({ a: 1, b: 2 }), option.none) +``` + Added in v2.0.0 ## ~~empty~~ diff --git a/src/ReadonlyRecord.ts b/src/ReadonlyRecord.ts index 698f6a276..328b816b2 100644 --- a/src/ReadonlyRecord.ts +++ b/src/ReadonlyRecord.ts @@ -1,4 +1,9 @@ /** + * The `ReadonlyRecord.ts` module enables dealing in a functional way with + * Typescript's `Readonly>` type. That is similar to the + * `Record.ts` module, but for a record with all properties + * declared as `readonly`. + * * @since 2.5.0 */ import { Applicative, Applicative1, Applicative2, Applicative2C, Applicative3, Applicative3C } from './Applicative' @@ -44,12 +49,32 @@ export type ReadonlyRecord = Readonly> // ------------------------------------------------------------------------------------- /** + * Builds a `ReadonlyRecord` by copying a `Record`. + * + * @example + * import { ReadonlyRecord, fromRecord } from "fp-ts/ReadonlyRecord" + * + * const x: Record = { a: 1, b: 2 }; + * const y: ReadonlyRecord = fromRecord(x); + * assert.deepStrictEqual(x,y); + * // `y.a = 5` gives compiler error + * * @category interop * @since 2.5.0 */ export const fromRecord = (r: Record): ReadonlyRecord => Object.assign({}, r) /** + * Builds a mutable `Record` from a `ReadonlyRecord`. + * + * @example + * import { ReadonlyRecord, toRecord } from "fp-ts/ReadonlyRecord" + * + * const x: ReadonlyRecord = { a: 1, b: 2 }; + * const y: Record = toRecord(x); + * assert.deepStrictEqual(x,y); + * y.a = 5; // it's ok, y is mutable + * * @category interop * @since 2.5.0 */ @@ -58,6 +83,11 @@ export const toRecord = (r: ReadonlyRecord): Record(r: ReadonlyRecord): number => Object.keys(r).length @@ -65,6 +95,11 @@ export const size = (r: ReadonlyRecord): number => Object.keys(r). /** * Test whether a `ReadonlyRecord` is empty. * + * @example + * import { isEmpty } from "fp-ts/ReadonlyRecord" + * + * assert.deepStrictEqual(isEmpty({}), true); + * assert.deepStrictEqual(isEmpty({ a: 3 }), false); * @since 2.5.0 */ export const isEmpty = (r: ReadonlyRecord): boolean => { @@ -93,11 +128,9 @@ export const keys: (r: ReadonlyRecord) => Readonly * import { collect } from 'fp-ts/ReadonlyRecord' * import { Ord } from 'fp-ts/string' * - * const x: { readonly a: string, readonly b: boolean } = { a: 'c', b: false } - * assert.deepStrictEqual( - * collect(Ord)((key, val) => ({ key: key, value: val }))(x), - * [{ key: 'a', value: 'c' }, { key: 'b', value: false }] - * ) + * const f = (k: string, a: A) => `${k.toUpperCase()}-${a}`; + * const x = { c: 3, a: "foo", b: false }; + * assert.deepStrictEqual(collect(Ord)(f)(x), ["A-foo", "B-false", "C-3"]); * * @since 2.5.0 */ @@ -131,6 +164,16 @@ export function collect( /** * Get a sorted `ReadonlyArray` of the key/value pairs contained in a `ReadonlyRecord`. * + * @example + * import { toReadonlyArray } from 'fp-ts/ReadonlyRecord' + * + * const x = { c: 3, a: "foo", b: false }; + * assert.deepStrictEqual(toReadonlyArray(x), [ + * ["a", "foo"], + * ["b", false], + * ["c", 3], + * ]); + * * @since 2.5.0 */ export const toReadonlyArray: (r: ReadonlyRecord) => ReadonlyArray = @@ -140,6 +183,18 @@ export const toReadonlyArray: (r: ReadonlyRecord) => /** * Unfolds a `ReadonlyRecord` into a list of key/value pairs. * + * Given an `Unfoldable` class type `U` such as `array` or `readonlyArray`, + * it uses the `unfold` function to create an instance of `U`, + * providing an iterating function that iterates over each + * key/value pair in the record sorted alphabetically by key. + * + * @example + * import { array, readonlyArray } from 'fp-ts' + * import { toUnfoldable } from 'fp-ts/ReadonlyRecord' + * + * assert.deepStrictEqual(toUnfoldable(array)({ b: 2, a: 1 }),[ [ 'a', 1 ], [ 'b', 2 ]]) + * assert.deepStrictEqual(toUnfoldable(readonlyArray)({ b: 2, a: 1 }),[ [ 'a', 1 ], [ 'b', 2 ]]) + * * @category destructors * @since 2.5.0 */ @@ -160,6 +215,12 @@ export function toUnfoldable(U: Unfoldable): (r: ReadonlyRecord(k: string, a: A) => (r: ReadonlyRecord): * * Note. This function is not pipeable because is a `Refinement`. * + * @example + * import { has } from 'fp-ts/ReadonlyRecord' + * + * assert.deepStrictEqual(has("a", { a: 1, b: 2 }), true); + * assert.deepStrictEqual(has("c", { a: 1, b: 2 }), false); + * * @since 2.10.0 */ export const has = (k: string, r: ReadonlyRecord): k is K => _.has.call(r, k) @@ -184,6 +251,12 @@ export const has = (k: string, r: ReadonlyRecord): /** * Delete a key and value from a `ReadonlyRecord`. * + * @example + * import { deleteAt } from 'fp-ts/ReadonlyRecord' + * + * assert.deepStrictEqual(deleteAt("a")({ a: 1, b: 2 }), { b: 2 }); + * assert.deepStrictEqual(deleteAt("c")({ a: 1, b: 2 }), { a: 1, b: 2 }); + * * @category combinators * @since 2.5.0 */ @@ -202,6 +275,18 @@ export function deleteAt(k: string): (r: ReadonlyRecord) => Readon } /** + * Replace a key/value pair in a `ReadonlyRecord`. + * + * @returns If the specified key exists it returns an `Option` containing a new `Record` + * with the entry updated, otherwise it returns `None` + * + * @example + * import { updateAt } from 'fp-ts/ReadonlyRecord' + * import { option } from 'fp-ts' + * + * assert.deepStrictEqual(updateAt("a", 3)({ a: 1, b: 2 }), option.some({ a: 3, b: 2 })); + * assert.deepStrictEqual(updateAt("c", 3)({ a: 1, b: 2 }), option.none); + * * @since 2.5.0 */ export const updateAt = (k: string, a: A) => ( @@ -219,6 +304,18 @@ export const updateAt = (k: string, a: A) => ( } /** + * Applies a mapping function to one spcific key/value pair in a `ReadonlyRecord`. + * + * @returns If the specified key exists it returns an `Option` containing a new `Record` + * with the entry updated, otherwise it returns `None` + * + * @example + * import { modifyAt } from 'fp-ts/ReadonlyRecord' + * import { option } from 'fp-ts' + * + * assert.deepStrictEqual(modifyAt("a", (x: number) => x * 3)({ a: 1, b: 2 }), option.some({ a: 3, b: 2 })); + * assert.deepStrictEqual(modifyAt("c", (x: number) => x * 3)({ a: 1, b: 2 }), option.none); + * * @since 2.5.0 */ export const modifyAt = (k: string, f: (a: A) => A) => ( @@ -239,6 +336,16 @@ export const modifyAt = (k: string, f: (a: A) => A) => ( /** * Delete a key and value from a `ReadonlyRecord`, returning the value as well as the subsequent `ReadonlyRecord`. * + * @returns If the specified key exists it returns an `Option` containing a new `ReadonlyRecord` + * with the entry removed, otherwise it returns `None` + * + * @example + * import { pop } from 'fp-ts/ReadonlyRecord' + * import { option } from 'fp-ts' + * + * assert.deepStrictEqual(pop("a")({ a: 1, b: 2, c: 3 }), option.some([1, { b: 2, c: 3 }])); + * assert.deepStrictEqual(pop("x")({ a: 1, b: 2, c: 3 }), option.none); + * * @since 2.5.0 */ export function pop( @@ -256,7 +363,29 @@ export function pop(k: string): (r: ReadonlyRecord) => Option( /** * Lookup the value for a key in a `ReadonlyRecord`. * + * @returns If the specified key exists it returns an `Option` containing the value, + * otherwise it returns `None` + * + * @example + * import { lookup } from 'fp-ts/ReadonlyRecord' + * import { option } from 'fp-ts' + * + * assert.deepStrictEqual(lookup("b")({ a: "foo", b: "bar" }), option.some("bar")); + * assert.deepStrictEqual(lookup("c")({ a: "foo", b: "bar" }), option.none); + * * @since 2.5.0 */ export function lookup(k: string): (r: ReadonlyRecord) => Option @@ -312,6 +451,12 @@ export const empty: ReadonlyRecord = {} /** * Map a `ReadonlyRecord` passing the keys to the iterating function. * + * @example + * import { mapWithIndex } from "fp-ts/ReadonlyRecord"; + * + * const f = (k: string, n: number) => `${k.toUpperCase()}-${n}`; + * assert.deepStrictEqual(mapWithIndex(f)({ a: 3, b: 5 }), { a: "A-3", b: "B-5" }); + * * @category combinators * @since 2.5.0 */ @@ -335,6 +480,12 @@ export function mapWithIndex( /** * Map a `ReadonlyRecord` passing the values to the iterating function. * + * @example + * import { map } from "fp-ts/ReadonlyRecord"; + * + * const f = (n: number) => `-${n}-`; + * assert.deepStrictEqual(map(f)({ a: 3, b: 5 }), { a: "-3-", b: "-5-" }); + * * @category combinators * @since 2.5.0 */ @@ -344,6 +495,21 @@ export function map(f: (a: A) => B): (fa: ReadonlyRecord) => Re } /** + * Reduces a `ReadonlyRecord` passing each key/value pair to the iterating function. + * Entries are processed in the order, sorted by key according to + * the given `Ord`. + * + * @example + * import { reduceWithIndex } from "fp-ts/ReadonlyRecord"; + * import { Ord } from "fp-ts/string"; + * + * const x = { c: 3, a: "foo", b: false }; + * assert.deepStrictEqual(reduceWithIndex(Ord)([] as string[], (k, b, a) => [...b, `${k}-${a}`])(x), [ + * "a-foo", + * "b-false", + * "c-3", + * ]); + * * @since 2.5.0 */ export function reduceWithIndex( @@ -380,6 +546,20 @@ export function reduceWithIndex( } /** + * Map and fold a `ReadonlyRecord`. + * Map the `ReadonlyRecord` passing each key/value pair to the iterating function. + * Then fold the results using the provided `Monoid`. + * + * @example + * import { foldMapWithIndex } from "fp-ts/ReadonlyRecord"; + * import { Ord } from "fp-ts/string"; + * import { Monoid } from "fp-ts/Monoid"; + * + * const m: Monoid = { empty: "", concat: (x: string, y: string) => (x ? `${x} -> ${y}` : `${y}`) }; + * const f = (k:string, a: number) => `${k}-${a}` + * const x = { c: 3, a: 1, b: 2 }; + * assert.deepStrictEqual(foldMapWithIndex(Ord)(m)(f)(x), "a-1 -> b-2 -> c-3"); + * * @since 2.5.0 */ export function foldMapWithIndex( @@ -415,6 +595,21 @@ export function foldMapWithIndex( } /** + * Same as `reduceWithIndex`, but reduce starting from the right + * (i.e. in reverse order, from the last to the first entry according to + * the given `Ord`). + * + * @example + * import { reduceRightWithIndex } from "fp-ts/ReadonlyRecord"; + * import { Ord } from "fp-ts/string"; + * + * const x = { c: 3, a: "foo", b: false }; + * assert.deepStrictEqual(reduceRightWithIndex(Ord)([] as string[], (k, a, b) => [...b, `${k}-${a}`])(x), [ + * "c-3", + * "b-false", + * "a-foo", + * ]); + * * @since 2.5.0 */ export function reduceRightWithIndex( @@ -453,6 +648,11 @@ export function reduceRightWithIndex( /** * Create a `ReadonlyRecord` with one key/value pair. * + * @example + * import { singleton } from "fp-ts/ReadonlyRecord"; + * + * assert.deepStrictEqual(singleton("a", 1), { a: 1 }); + * * @category constructors * @since 2.5.0 */ @@ -533,6 +733,30 @@ export function traverse( } /** + * `ReadonlyRecord` sequencing, + * i.e., take a `ReadonlyRecord` in which elements are monads + * and return a monad of a `ReadonlyRecord` of the base types. + * The following example for instance shows sequencing + * a `ReadonlyRecord>` + * into an `Option>`. + * + * `sequence` in `ReadonlyRecord` is equivalent to `sequenceS` in `Apply.ts`. + * + * @example + * import { sequence } from "fp-ts/ReadonlyRecord"; + * import { option } from "fp-ts"; + * import { sequenceS } from "fp-ts/Apply"; + * + * assert.deepStrictEqual( + * sequence(option.Applicative)({ a: option.some(1), b: option.some(2) }), + * option.some({ a: 1, b: 2 }) + * ); + * assert.deepStrictEqual(sequence(option.Applicative)({ a: option.some(1), b: option.none }), option.none); + * assert.deepStrictEqual( + * sequence(option.Applicative)({ a: option.some(1), b: option.some(2) }), + * sequenceS(option.Applicative)({ a: option.some(1), b: option.some(2) }) + * ); + * * @since 2.5.0 */ export function sequence( @@ -584,6 +808,25 @@ export const wilt: PipeableWilt1 = ( } /** + * Maps a `ReadonlyRecord` with a function returning an `Either` and + * partitions the resulting `ReadonlyRecord` into `Left`s and `Right`s. + * + * @example + * import { partitionMapWithIndex } from "fp-ts/ReadonlyRecord" + * import { either } from "fp-ts" + * + * const f = (key: string, a: number) => + * a >= 0 ? either.right(`${key} is >= 0 (${a})`) : either.left(`${key} is < 0 (${a})`); + * assert.deepStrictEqual(partitionMapWithIndex(f)({ a: -1, b: 2, c: 123 }), { + * left: { + * a: "a is < 0 (-1)", + * }, + * right: { + * b: "b is >= 0 (2)", + * c: "c is >= 0 (123)", + * }, + * }); + * * @since 2.5.0 */ export function partitionMapWithIndex( @@ -613,6 +856,25 @@ export function partitionMapWithIndex( } /** + * Partition a `ReadonlyRecord` into two parts according to a predicate + * that takes a key and a value. + * + * @example + * import { partitionWithIndex } from "fp-ts/ReadonlyRecord" + * + * assert.deepStrictEqual( + * partitionWithIndex((key: string, a: number) => key.length <= 1 && a > 0)({ a: -1, b: 2, ccc: 7 }), + * { + * left: { + * a: -1, + * ccc: 7, + * }, + * right: { + * b: 2, + * }, + * } + * ); + * * @since 2.5.0 */ export function partitionWithIndex( @@ -645,6 +907,19 @@ export function partitionWithIndex( } /** + * Maps a `ReadonlyRecord` with an iterating function that takes key and value and + * returns an `Option`, keeping only the `Some` values and discarding `None`s. + * + * @example + * import { filterMapWithIndex } from "fp-ts/ReadonlyRecord" + * import { option } from "fp-ts" + * + * const f = (key: string, a: number) => (a >= 0 ? option.some(`${key}${a}`) : option.none); + * assert.deepStrictEqual(filterMapWithIndex(f)({ a: -1, b: 2, c: 3 }), { + * b: "b2", + * c: "c3", + * }); + * * @category combinators * @since 2.5.0 */ @@ -669,6 +944,19 @@ export function filterMapWithIndex( } /** + * Produce a new `ReadonlyRecord` keeping only the entries that satisfy + * a predicate taking key and value as input. + * + * @example + * import { filterWithIndex } from "fp-ts/ReadonlyRecord" + * + * assert.deepStrictEqual( + * filterWithIndex((s: string, v: number) => s.length <= 1 && v > 0)({ a: 1, b: -2, ccc: 3 }), + * { + * a: 1, + * } + * ); + * * @since 2.5.0 */ export function filterWithIndex( @@ -795,6 +1083,14 @@ export function fromFoldableMap( } /** + * Test if every value in a `ReadonlyRecord` satisfies the predicate. + * + * @example + * import { every } from "fp-ts/ReadonlyRecord" + * + * assert.deepStrictEqual(every((n: number) => n >= 0)({ a: 1, b: 2 }), true); + * assert.deepStrictEqual(every((n: number) => n >= 0)({ a: 1, b: -1 }), false); + * * @since 2.5.0 */ export function every(predicate: Predicate): (r: ReadonlyRecord) => boolean { @@ -809,6 +1105,14 @@ export function every(predicate: Predicate): (r: ReadonlyRecord } /** + * Test if at least one value in a `ReadonlyRecord` satisfies the predicate. + * + * @example + * import { some } from "fp-ts/ReadonlyRecord" + * + * assert.deepStrictEqual(some((n: number) => n >= 0)({ a: 1, b: -2 }), true); + * assert.deepStrictEqual(some((n: number) => n >= 0)({ a: -1, b: -2 }), false); + * * @since 2.5.0 */ export function some(predicate: (a: A) => boolean): (r: ReadonlyRecord) => boolean { @@ -824,6 +1128,16 @@ export function some(predicate: (a: A) => boolean): (r: ReadonlyRecord( @@ -850,6 +1164,21 @@ export function elem( } /** + * Union of two `ReadonlyRecord`s. + * Takes two `ReadonlyRecord`s and produces a `ReadonlyRecord` combining all the + * entries of the two inputs. + * It uses the `concat` function of the provided `Magma` to + * combine the elements with the same key. + * + * @example + * import { union } from "fp-ts/ReadonlyRecord"; + * import { Magma } from "fp-ts/Magma"; + * + * const m1: Magma = { concat: (x: number, y: number) => x + y }; + * assert.deepStrictEqual(union(m1)({ a: 3, c: 3 })({ a: 1, b: 2 }), { a: 4, b: 2, c: 3 }); + * const m2: Magma = { concat: (x: number) => x }; + * assert.deepStrictEqual(union(m2)({ a: 3, c: 3 })({ a: 1, b: 2 }), { a: 1, b: 2, c: 3 }); + * * @category combinators * @since 2.11.0 */ @@ -879,6 +1208,21 @@ export const union = (M: Magma) => (second: ReadonlyRecord) => } /** + * Intersection of two `ReadonlyRecord`s. + * Takes two `ReadonlyRecord`s and produces a `ReadonlyRecord` combining only the + * entries of the two inputswith the same key. + * It uses the `concat` function of the provided `Magma` to + * combine the elements. + * + * @example + * import { intersection } from "fp-ts/ReadonlyRecord"; + * import { Magma } from "fp-ts/Magma"; + * + * const m1: Magma = { concat: (x: number, y: number) => x + y }; + * assert.deepStrictEqual(intersection(m1)({ a: 3, c: 3 })({ a: 1, b: 2 }), { a: 4}); + * const m2: Magma = { concat: (x: number) => x }; + * assert.deepStrictEqual(intersection(m2)({ a: 3, c: 3 })({ a: 1, b: 2 }), { a: 1}); + * * @category combinators * @since 2.11.0 */ @@ -898,6 +1242,18 @@ export const intersection = (M: Magma) => (second: ReadonlyRecord) => ( // ------------------------------------------------------------------------------------- /** + * Given a `Predicate`, it produces a new `ReadonlyRecord` keeping only the entries with a + * value that satisfies the provided predicate. + * + * @example + * import { filter } from "fp-ts/ReadonlyRecord" + * + * assert.deepStrictEqual(filter((s: string) => s.length < 4)({ a: "foo", b: "bar", c: "verylong" }), { + * a: "foo", + * b: "bar", + * }); + * * @category Filterable * @since 2.5.0 */ @@ -1069,6 +1436,19 @@ export const filter: { filterWithIndex((_, a) => predicate(a)) /** + * Maps a `ReadonlyRecord` with an iterating function that returns an `Option` + * and it keeps only the `Some` values discarding the `None`s. + * + * @example + * import { filterMap } from "fp-ts/ReadonlyRecord" + * import { option } from "fp-ts" + * + * const f = (s: string) => s.length < 4 ? option.some(`${s} is short`): option.none + * assert.deepStrictEqual(filterMap(f)({ a: "foo", b: "bar", c: "verylong" }), { + * a: "foo is short", + * b: "bar is short", + * }); + * * @category Filterable * @since 2.5.0 */ @@ -1077,6 +1457,21 @@ export const filterMap: ( ) => (fa: ReadonlyRecord) => ReadonlyRecord = (f) => filterMapWithIndex((_, a) => f(a)) /** + * Partition a `ReadonlyRecord` into two parts according to a `Predicate`. + * + * @example + * import { partition } from "fp-ts/ReadonlyRecord" + * + * assert.deepStrictEqual(partition((s: string) => s.length < 4)({ a: "foo", b: "bar", c: "verylong" }), { + * left:{ + * c: "verylong" + * }, + * right: { + * a: "foo", + * b: "bar", + * }, + * }); + * * @category Filterable * @since 2.5.0 */ @@ -1096,6 +1491,24 @@ export const partition: { partitionWithIndex((_, a) => predicate(a)) /** + * Maps a `ReadonlyRecord` with a function returning an `Either` and + * partitions the resulting `ReadonlyRecord` into `Left`s and `Right`s. + * + * @example + * import { partitionMap } from "fp-ts/ReadonlyRecord" + * import { either } from "fp-ts" + * + * const f = (s: string) => (s.length < 4 ? either.right(`${s} is short`) : either.left(`${s} is not short`)); + * assert.deepStrictEqual(partitionMap(f)({ a: "foo", b: "bar", c: "verylong" }), { + * left: { + * c: "verylong is not short", + * }, + * right: { + * a: "foo is short", + * b: "bar is short", + * }, + * }); + * * @category Filterable * @since 2.5.0 */ @@ -1105,6 +1518,21 @@ export const partitionMap: ( partitionMapWithIndex((_, a) => f(a)) /** + * Reduces a `ReadonlyRecord` passing each value to the iterating function. + * Entries are processed in order, sorted by key according to + * the given `Ord`. + * + * @example + * import { reduce } from "fp-ts/ReadonlyRecord"; + * import { Ord } from "fp-ts/string"; + * + * const x = { c: 3, a: "foo", b: false }; + * assert.deepStrictEqual(reduce(Ord)([] as string[], (b, a) => [...b, `-${a}-`])(x), [ + * "-foo-", + * "-false-", + * "-3-", + * ]); + * * @category Foldable * @since 2.5.0 */ @@ -1126,6 +1554,20 @@ export function reduce( } /** + * Map and fold a `ReadonlyRecord`. + * Map the `ReadonlyRecord` passing each value to the iterating function. + * Then fold the results using the provided `Monoid`. + * + * @example + * import { foldMap } from "fp-ts/ReadonlyRecord"; + * import { Ord } from "fp-ts/string"; + * import { Monoid } from "fp-ts/Monoid"; + * + * const m: Monoid = { empty: "", concat: (x: string, y: string) => (x ? `${x} -> ${y}` : `${y}`) }; + * const f = (a: number) => `-${a}-`; + * const x = { c: 3, a: 1, b: 2 }; + * assert.deepStrictEqual(foldMap(Ord)(m)(f)(x), "-1- -> -2- -> -3-"); + * * @category Foldable * @since 2.5.0 */ @@ -1154,6 +1596,21 @@ export function foldMap( } /** + * Same as `reduce` but entries are processed _from the right_, + * i.e. in reverse order, from the last to the first entry, according to + * the given `Ord`. + * + * @example + * import { reduceRight } from "fp-ts/ReadonlyRecord"; + * import { Ord } from "fp-ts/string"; + * + * const x = { c: 3, a: "foo", b: false }; + * assert.deepStrictEqual(reduceRight(Ord)([] as string[], (a, b) => [...b, `-${a}-`])(x), [ + * "-3-", + * "-false-", + * "-foo-", + * ]); + * * @category Foldable * @since 2.5.0 */ @@ -1175,6 +1632,18 @@ export function reduceRight( } /** + * Compact a `ReadonlyRecord` of `Option`s discarding the `None` values and + * keeping the `Some` values. + * + * @example + * import { compact } from 'fp-ts/ReadonlyRecord' + * import { option } from 'fp-ts' + * + * assert.deepStrictEqual(compact({ a: option.some("foo"), b: option.none, c: option.some("bar") }), { + * a: "foo", + * c: "bar", + * }); + * * @category Compactable * @since 2.5.0 */ @@ -1192,6 +1661,25 @@ export const compact = (r: ReadonlyRecord>): ReadonlyRecord } /** + * Separate a `ReadonlyRecord` of `Either`s into `Left`s and `Right`s. + * + * @example + * import { separate } from 'fp-ts/ReadonlyRecord' + * import { either } from 'fp-ts' + * + * assert.deepStrictEqual( + * separate({ a: either.right("foo"), b: either.left("bar"), c: either.right("baz") }), + * { + * right: { + * a: "foo", + * c: "baz", + * }, + * left: { + * b: "bar", + * }, + * } + * ); + * * @category Compactable * @since 2.5.0 */ @@ -1236,6 +1724,19 @@ declare module './HKT' { } /** + * Produces a `Show` for a `ReadonlyRecord`, given a `Show` for the base type + * (a `Show` produces a human-readable representation of an instance). + * `ReadonlyRecord` entries are sorted by key with the provided `Ord`. + * + * @example + * import { getShow, ReadonlyRecord } from "fp-ts/ReadonlyRecord" + * import { Show } from "fp-ts/Show" + * import { Ord } from "fp-ts/string" + * + * const sNumber: Show = { show: (n: number) => `${n}` }; + * const sRecord: Show> = getShow(Ord)(sNumber); + * assert.deepStrictEqual(sRecord.show({ b: 2, a: 1 }), '{ "a": 1, "b": 2 }'); + * * @category instances * @since 2.5.0 */ @@ -1262,6 +1763,18 @@ export function getShow( } /** + * Given an `Eq` for the base type, it produces an `Eq` + * for a `ReadonlyRecord` of that base type. + * + * @example + * import { getEq, ReadonlyRecord } from "fp-ts/ReadonlyRecord"; + * import { string } from "fp-ts"; + * import { Eq } from "fp-ts/Eq"; + * + * const eq: Eq> = getEq(string.Eq); + * assert.deepStrictEqual(eq.equals({ a: "foo" }, { b: "bar" }), false); + * assert.deepStrictEqual(eq.equals({ a: "foo" }, { a: "foo" }), true); + * * @category instances * @since 2.5.0 */ @@ -1272,14 +1785,17 @@ export function getEq(E: Eq): Eq> { } /** - * Returns a `Monoid` instance for `ReadonlyRecord`s given a `Semigroup` instance for their values. + * Returns a `Monoid` instance for `ReadonlyRecord`s, given a `Semigroup` + * instance for the base type. + * The `Monoid` makes the union of two `ReadonlyRecord`s comining the + * overlapping entries with the provided `Semigroup`. * * @example * import { SemigroupSum } from 'fp-ts/number' * import { getMonoid } from 'fp-ts/ReadonlyRecord' * - * const M = getMonoid(SemigroupSum) - * assert.deepStrictEqual(M.concat({ foo: 123 }, { foo: 456 }), { foo: 579 }) + * const M = getMonoid(SemigroupSum); + * assert.deepStrictEqual(M.concat({ foo: 123, bar: 234 }, { foo: 456, baz: 567 }), { foo: 579 , bar: 234, baz: 567 }); * * @category instances * @since 2.5.0 @@ -1317,6 +1833,17 @@ export const Functor: Functor1 = { /** * Derivable from `Functor`. + * Takes a value and a `ReadonlyRecord` of functions and returns a + * `ReadonlyRecord` by applying each function to the input value. + * + * @example + * import { flap } from "fp-ts/ReadonlyRecord" + * + * const fab = { x: (n: number) => `${n} times 2`, y: (n: number) => `${n * 2}` }; + * assert.deepStrictEqual(flap(3)(fab), { + * x: "3 times 2", + * y: "6", + * }); * * @category combinators * @since 2.10.0 @@ -1336,6 +1863,9 @@ export const FunctorWithIndex: FunctorWithIndex1 = { } /** + * Produces a `Foldable` instance for a `ReadonlyRecord`, using the + * provided `Ord` to sort the `ReadonlyRecord`'s entries by key. + * * @category instances * @since 2.11.0 */ @@ -1347,6 +1877,9 @@ export const getFoldable = (O: Ord): Foldable1 => ({ }) /** + * Produces a `FoldableWithIndex1` instance for a `ReadonlyRecord`, using the + * provided `Ord` to sort the `ReadonlyRecord`'s entries by key. + * * @category instances * @since 2.11.0 */ @@ -1406,6 +1939,9 @@ export const FilterableWithIndex: FilterableWithIndex1 = { } /** + * Produces a `Traversable` instance for a `ReadonlyRecord`, using the + * provided `Ord` to sort the `ReadonlyRecord`'s entries by key. + * * @category instances * @since 2.11.0 */ @@ -1420,6 +1956,9 @@ export const getTraversable = (O: Ord): Traversable1 => ({ }) /** + * Produces a `TraversableWithIndex` instance for a `ReadonlyRecord`, using the + * provided `Ord` to sort the `ReadonlyRecord`'s entries by key. + * * @category instances * @since 2.11.0 */ @@ -1464,6 +2003,19 @@ export const getWitherable = (O: Ord): Witherable1 => { } /** + * Given a `Semigroup` in the base type, it produces a `Semigroup` + * in the `ReadonlyRecord` of the base type. + * The resulting `Semigroup` concatenates two `ReadonlyRecord`s by + * `union`. + * + * @example + * import { getUnionSemigroup, ReadonlyRecord } from "fp-ts/ReadonlyRecord" + * import { Semigroup } from "fp-ts/Semigroup" + * + * const sNumber: Semigroup = { concat: (x, y) => x - y }; + * const sReadonlyRecord: Semigroup> = getUnionSemigroup(sNumber); + * assert.deepStrictEqual(sReadonlyRecord.concat({ a: 1, b: 2 }, { b: 3, c: 4 }), { a: 1, b: -1, c: 4 }); + * * @category instances * @since 2.11.0 */ @@ -1475,6 +2027,19 @@ export const getUnionSemigroup = (S: Semigroup): Semigroup(S: Semigroup): Monoid = { concat: (x, y) => x - y }; + * const sReadonlyRecord: Semigroup> = getIntersectionSemigroup(sNumber); + * assert.deepStrictEqual(sReadonlyRecord.concat({ a: 1, b: 2 }, { b: 3, c: 4 }), { b: -1 }); + * * @category instances * @since 2.11.0 */ @@ -1495,6 +2073,19 @@ export const getIntersectionSemigroup = (S: Semigroup): Semigroup> = getDifferenceMagma(); + * assert.deepStrictEqual(m.concat(r1, r2), difference(r2)(r1)); + * assert.deepStrictEqual(m.concat(r1, r2), { c: 3, b: 2 }); + * * @category instances * @since 2.11.0 */ diff --git a/src/Record.ts b/src/Record.ts index 03efa8861..5d88f94ac 100644 --- a/src/Record.ts +++ b/src/Record.ts @@ -1,4 +1,7 @@ /** + * The `Record` module enables dealing with Typescript's `Record` + * type in a functional way, basically treating it as a `Functor` in `T`. + * * @since 2.0.0 */ import { Applicative, Applicative1, Applicative2, Applicative2C, Applicative3, Applicative3C } from './Applicative' @@ -37,6 +40,11 @@ import { PipeableWilt1, PipeableWither1, wiltDefault, Witherable1, witherDefault /** * Calculate the number of key/value pairs in a `Record`. * + * @example + * import { size } from "fp-ts/Record"; + * + * assert.deepStrictEqual(size({ a: true, b: 2, c: "three" }), 3); + * * @since 2.0.0 */ export const size: (r: Record) => number = RR.size @@ -44,6 +52,12 @@ export const size: (r: Record) => number = RR.size /** * Test whether a `Record` is empty. * + * @example + * import { isEmpty } from "fp-ts/Record"; + * + * assert.deepStrictEqual(isEmpty({}), true); + * assert.deepStrictEqual(isEmpty({ a: 3 }), false); + * * @since 2.0.0 */ export const isEmpty: (r: Record) => boolean = RR.isEmpty @@ -52,6 +66,13 @@ const keys_ = (O: Ord) => (r: Record): Arr (Object.keys(r) as any).sort(O.compare) /** + * The keys of a `Record`, sorted alphabetically. + * + * @example + * import { keys } from "fp-ts/Record"; + * + * assert.deepStrictEqual(keys({ c: 1, a: 2, b: 3 }), ["a", "b", "c"]); + * * @since 2.0.0 */ export const keys: (r: Record) => Array = @@ -60,16 +81,23 @@ export const keys: (r: Record) => Array = /** * Map a `Record` into an `Array`. + * It passes each key/value pair to the iterating function and collects + * the results in an array, sorted alphabetically by the original key. * * @example * import { collect } from 'fp-ts/Record' * import { Ord } from 'fp-ts/string' * - * const x: { readonly a: string, readonly b: boolean } = { a: 'c', b: false } + * const f = (k: string, a: A) => `${k.toUpperCase()}-${a}`; + * const x = { c: 3, a: "foo", b: false }; * assert.deepStrictEqual( - * collect(Ord)((key, val) => ({ key: key, value: val }))(x), - * [{ key: 'a', value: 'c' }, { key: 'b', value: false }] - * ) + * collect(Ord)(f)(x), + * [ + * "A-foo", + * "B-false", + * "C-3", + * ] + * ); * * @since 2.0.0 */ @@ -100,6 +128,17 @@ export function collect( /** * Get a sorted `Array` of the key/value pairs contained in a `Record`. + * Sorted alphabetically by key. + * + * @example + * import { toArray } from 'fp-ts/Record' + * + * const x = { c: 3, a: "foo", b: false }; + * assert.deepStrictEqual(toArray(x), [ + * ["a", "foo"], + * ["b", false], + * ["c", 3], + * ]); * * @since 2.0.0 */ @@ -110,6 +149,18 @@ export const toArray: (r: Record) => Array<[K, A]> = /** * Unfolds a `Record` into a list of key/value pairs. * + * Given an `Unfoldable` class type `U` such as `array` or `readonlyArray`, + * it uses the `unfold` function to create an instance of `U`, + * providing an iterating function that iterates over each + * key/value pair in the record sorted alphabetically by key. + * + * @example + * import { array, readonlyArray } from 'fp-ts' + * import { toUnfoldable } from 'fp-ts/Record' + * + * assert.deepStrictEqual(toUnfoldable(array)({ b: 2, a: 1 }),[ [ 'a', 1 ], [ 'b', 2 ]]) + * assert.deepStrictEqual(toUnfoldable(readonlyArray)({ b: 2, a: 1 }),[ [ 'a', 1 ], [ 'b', 2 ]]) + * * @since 2.0.0 */ export function toUnfoldable( @@ -127,6 +178,12 @@ export function toUnfoldable(U: Unfoldable): (r: Record) => /** * Insert or replace a key/value pair in a `Record`. * + * @example + * import { upsertAt } from 'fp-ts/Record' + * + * assert.deepStrictEqual(upsertAt("a", 5)({ a: 1, b: 2 }), { a: 5, b: 2 }); + * assert.deepStrictEqual(upsertAt("c", 5)({ a: 1, b: 2 }), { a: 1, b: 2, c: 5 }); + * * @category combinators * @since 2.10.0 */ @@ -137,6 +194,12 @@ export const upsertAt: (k: string, a: A) => (r: Record) => Record< * * Note. This function is not pipeable because is a `Refinement`. * + * @example + * import { has } from 'fp-ts/Record' + * + * assert.deepStrictEqual(has("a", { a: 1, b: 2 }), true); + * assert.deepStrictEqual(has("c", { a: 1, b: 2 }), false); + * * @since 2.10.0 */ export const has: (k: string, r: Record) => k is K = RR.has @@ -144,6 +207,12 @@ export const has: (k: string, r: Record) => k is K /** * Delete a key and value from a `Record`. * + * @example + * import { deleteAt } from 'fp-ts/Record' + * + * assert.deepStrictEqual(deleteAt("a")({ a: 1, b: 2 }), { b: 2 }); + * assert.deepStrictEqual(deleteAt("c")({ a: 1, b: 2 }), { a: 1, b: 2 }); + * * @since 2.0.0 */ export function deleteAt( @@ -161,12 +230,36 @@ export function deleteAt(k: string): (r: Record) => Record(k: string, a: A): ((r: Record) => Option>) => modifyAt(k, () => a) /** + * Applies a mapping function to one spcific key/value pair in a `Record`. + * + * @returns If the specified key exists it returns an `Option` containing a new `Record` + * with the entry updated, otherwise it returns `None` + * + * @example + * import { modifyAt } from 'fp-ts/Record' + * import { option } from 'fp-ts' + * + * assert.deepStrictEqual(modifyAt("a", (x: number) => x * 3)({ a: 1, b: 2 }), option.some({ a: 3, b: 2 })); + * assert.deepStrictEqual(modifyAt("c", (x: number) => x * 3)({ a: 1, b: 2 }), option.none); + * * @since 2.0.0 */ export const modifyAt = (k: string, f: (a: A) => A) => (r: Record): Option> => { @@ -181,6 +274,16 @@ export const modifyAt = (k: string, f: (a: A) => A) => (r: /** * Delete a key and value from a `Record`, returning the value as well as the subsequent `Record`. * + * @returns If the specified key exists it returns an `Option` containing a new `Record` + * with the entry removed, otherwise it returns `None` + * + * @example + * import { pop } from 'fp-ts/Record' + * import { option } from 'fp-ts' + * + * assert.deepStrictEqual(pop("a")({ a: 1, b: 2, c: 3 }), option.some([1, { b: 2, c: 3 }])); + * assert.deepStrictEqual(pop("x")({ a: 1, b: 2, c: 3 }), option.none); + * * @since 2.0.0 */ export function pop( @@ -196,7 +299,29 @@ export function pop(k: string): (r: Record) => Option<[A, Record( /** * Lookup the value for a key in a `Record`. * + * @returns If the specified key exists it returns an `Option` containing the value, + * otherwise it returns `None` + * + * @example + * import { lookup } from 'fp-ts/Record' + * import { option } from 'fp-ts' + * + * assert.deepStrictEqual(lookup("b")({ a: "foo", b: "bar" }), option.some("bar")); + * assert.deepStrictEqual(lookup("c")({ a: "foo", b: "bar" }), option.none); + * * @since 2.0.0 */ export const lookup: { @@ -219,7 +354,13 @@ export const lookup: { } = RR.lookup /** - * Map a `Record` passing the keys to the iterating function. + * Map a `Record` passing the key/value pairs to the iterating function. + * + * @example + * import { mapWithIndex } from "fp-ts/Record"; + * + * const f = (k: string, n: number) => `${k.toUpperCase()}-${n}`; + * assert.deepStrictEqual(mapWithIndex(f)({ a: 3, b: 5 }), { a: "A-3", b: "B-5" }); * * @since 2.0.0 */ @@ -229,11 +370,32 @@ export const mapWithIndex: (f: (k: K, a: A) => B) => (fa /** * Map a `Record` passing the values to the iterating function. * + * @example + * import { map } from "fp-ts/Record"; + * + * const f = (n: number) => `-${n}-`; + * assert.deepStrictEqual(map(f)({ a: 3, b: 5 }), { a: "-3-", b: "-5-" }); + * * @since 2.0.0 */ export const map: (f: (a: A) => B) => (fa: Record) => Record = RR.map /** + * Reduces a `Record` passing each key/value pair to the iterating function. + * Entries are processed in the order, sorted by key according to + * the given `Ord`. + * + * @example + * import { reduceWithIndex } from "fp-ts/Record"; + * import { Ord } from "fp-ts/string"; + * + * const x = { c: 3, a: "foo", b: false }; + * assert.deepStrictEqual(reduceWithIndex(Ord)([] as string[], (k, b, a) => [...b, `${k}-${a}`])(x), [ + * "a-foo", + * "b-false", + * "c-3", + * ]); + * * @since 2.0.0 */ export function reduceWithIndex( @@ -252,6 +414,20 @@ export function reduceWithIndex( } /** + * Map and fold a `Record`. + * Map the `Record` passing each key/value pair to the iterating function. + * Then fold the results using the provided `Monoid`. + * + * @example + * import { foldMapWithIndex } from "fp-ts/Record"; + * import { Ord } from "fp-ts/string"; + * import { Monoid } from "fp-ts/Monoid"; + * + * const m: Monoid = { empty: "", concat: (x: string, y: string) => (x ? `${x} -> ${y}` : `${y}`) }; + * const f = (k:string, a: number) => `${k}-${a}` + * const x = { c: 3, a: 1, b: 2 }; + * assert.deepStrictEqual(foldMapWithIndex(Ord)(m)(f)(x), "a-1 -> b-2 -> c-3"); + * * @since 2.0.0 */ export function foldMapWithIndex( @@ -274,6 +450,21 @@ export function foldMapWithIndex( } /** + * Same as `reduceWithIndex`, but reduce starting from the right + * (i.e. in reverse order, from the last to the first entry according to + * the given `Ord`). + * + * @example + * import { reduceRightWithIndex } from "fp-ts/Record"; + * import { Ord } from "fp-ts/string"; + * + * const x = { c: 3, a: "foo", b: false }; + * assert.deepStrictEqual(reduceRightWithIndex(Ord)([] as string[], (k, a, b) => [...b, `${k}-${a}`])(x), [ + * "c-3", + * "b-false", + * "a-foo", + * ]); + * * @since 2.0.0 */ export function reduceRightWithIndex( @@ -294,6 +485,11 @@ export function reduceRightWithIndex( /** * Create a `Record` with one key/value pair. * + * @example + * import { singleton } from "fp-ts/Record"; + * + * assert.deepStrictEqual(singleton("a", 1), { a: 1 }); + * * @since 2.0.0 */ export const singleton: (k: string, a: A) => Record = RR.singleton @@ -357,6 +553,30 @@ export function traverse( } /** + * `Record` sequencing, + * i.e., take a `Record` in which elements are monads + * and return a monad of a `Record` of the base types. + * The following example for instance shows sequencing + * a `Record>` + * into an `Option>`. + * + * `sequence` in `Record` is equivalent to `sequenceS` in `Apply.ts`. + * + * @example + * import { sequence } from "fp-ts/Record"; + * import { option } from "fp-ts"; + * import { sequenceS } from "fp-ts/Apply"; + * + * assert.deepStrictEqual( + * sequence(option.Applicative)({ a: option.some(1), b: option.some(2) }), + * option.some({ a: 1, b: 2 }) + * ); + * assert.deepStrictEqual(sequence(option.Applicative)({ a: option.some(1), b: option.none }), option.none); + * assert.deepStrictEqual( + * sequence(option.Applicative)({ a: option.some(1), b: option.some(2) }), + * sequenceS(option.Applicative)({ a: option.some(1), b: option.some(2) }) + * ); + * * @since 2.0.0 */ export function sequence( @@ -404,6 +624,25 @@ export const wilt: PipeableWilt1 = ( } /** + * Maps a `Record` with a function returning an `Either` and + * partitions the resulting `Record` into `Left`s and `Right`s. + * + * @example + * import { partitionMapWithIndex } from "fp-ts/Record" + * import { either } from "fp-ts" + * + * const f = (key: string, a: number) => + * a >= 0 ? either.right(`${key} is >= 0 (${a})`) : either.left(`${key} is < 0 (${a})`); + * assert.deepStrictEqual(partitionMapWithIndex(f)({ a: -1, b: 2, c: 123 }), { + * left: { + * a: "a is < 0 (-1)", + * }, + * right: { + * b: "b is >= 0 (2)", + * c: "c is >= 0 (123)", + * }, + * }); + * * @since 2.0.0 */ export const partitionMapWithIndex: ( @@ -411,6 +650,25 @@ export const partitionMapWithIndex: ( ) => (fa: Record) => Separated, Record> = RR.partitionMapWithIndex /** + * Partition a `Record` into two parts according to a predicate + * that takes a key and a value. + * + * @example + * import { partitionWithIndex } from "fp-ts/Record" + * + * assert.deepStrictEqual( + * partitionWithIndex((key: string, a: number) => key.length <= 1 && a > 0)({ a: -1, b: 2, ccc: 7 }), + * { + * left: { + * a: -1, + * ccc: 7, + * }, + * right: { + * b: 2, + * }, + * } + * ); + * * @since 2.0.0 */ export function partitionWithIndex( @@ -429,6 +687,19 @@ export function partitionWithIndex( } /** + * Maps a `Record` with an iterating function that takes key and value and + * returns an `Option`, keeping only the `Some` values and discarding `None`s. + * + * @example + * import { filterMapWithIndex } from "fp-ts/Record" + * import { option } from "fp-ts" + * + * const f = (key: string, a: number) => (a >= 0 ? option.some(`${key}${a}`) : option.none); + * assert.deepStrictEqual(filterMapWithIndex(f)({ a: -1, b: 2, c: 3 }), { + * b: "b2", + * c: "c3", + * }); + * * @since 2.0.0 */ export const filterMapWithIndex: ( @@ -436,6 +707,19 @@ export const filterMapWithIndex: ( ) => (fa: Record) => Record = RR.filterMapWithIndex /** + * Produce a new `Record` keeping only the entries that satisfy + * a predicate taking key and value as input. + * + * @example + * import { filterWithIndex } from "fp-ts/Record" + * + * assert.deepStrictEqual( + * filterWithIndex((s: string, v: number) => s.length <= 1 && v > 0)({ a: 1, b: -2, ccc: 3 }), + * { + * a: 1, + * } + * ); + * * @since 2.0.0 */ export function filterWithIndex( @@ -535,17 +819,43 @@ export function fromFoldableMap( } /** + * Test if every value in a `Record` satisfies the predicate. + * + * @example + * import { every } from "fp-ts/Record" + * + * assert.deepStrictEqual(every((n: number) => n >= 0)({ a: 1, b: 2 }), true); + * assert.deepStrictEqual(every((n: number) => n >= 0)({ a: 1, b: -1 }), false); + * * @since 2.0.0 */ export const every: (predicate: Predicate) => (r: Record) => boolean = RR.every /** + * Test if at least one value in a `Record` satisfies the predicate. + * + * @example + * import { some } from "fp-ts/Record" + * + * assert.deepStrictEqual(some((n: number) => n >= 0)({ a: 1, b: -2 }), true); + * assert.deepStrictEqual(some((n: number) => n >= 0)({ a: -1, b: -2 }), false); + * * @since 2.0.0 */ export const some: (predicate: (a: A) => boolean) => (r: Record) => boolean = RR.some // TODO: remove non-curried overloading in v3 /** + * Given an `Eq` checks if a `Record` contains an entry with + * value equal to a provided value. + * + * @example + * import { elem } from "fp-ts/Record" + * import { number } from "fp-ts" + * + * assert.deepStrictEqual(elem(number.Eq)(123, { foo: 123, bar: 234 }), true); + * assert.deepStrictEqual(elem(number.Eq)(-7, { foo: 123, bar: 234 }), false); + * * @since 2.0.0 */ export const elem: ( @@ -556,6 +866,21 @@ export const elem: ( } = RR.elem /** + * Union of two `Record`s. + * Takes two `Record`s and produces a `Record` combining all the + * entries of the two inputs. + * It uses the `concat` function of the provided `Magma` to + * combine the elements with the same key. + * + * @example + * import { union } from "fp-ts/Record"; + * import { Magma } from "fp-ts/Magma"; + * + * const m1: Magma = { concat: (x: number, y: number) => x + y }; + * assert.deepStrictEqual(union(m1)({ a: 3, c: 3 })({ a: 1, b: 2 }), { a: 4, b: 2, c: 3 }); + * const m2: Magma = { concat: (x: number) => x }; + * assert.deepStrictEqual(union(m2)({ a: 3, c: 3 })({ a: 1, b: 2 }), { a: 1, b: 2, c: 3 }); + * * @category combinators * @since 2.11.0 */ @@ -575,6 +900,21 @@ export const union = ( } /** + * Intersection of two `Record`s. + * Takes two `Record`s and produces a `Record` combining only the + * entries of the two inputswith the same key. + * It uses the `concat` function of the provided `Magma` to + * combine the elements. + * + * @example + * import { intersection } from "fp-ts/Record"; + * import { Magma } from "fp-ts/Magma"; + * + * const m1: Magma = { concat: (x: number, y: number) => x + y }; + * assert.deepStrictEqual(intersection(m1)({ a: 3, c: 3 })({ a: 1, b: 2 }), { a: 4}); + * const m2: Magma = { concat: (x: number) => x }; + * assert.deepStrictEqual(intersection(m2)({ a: 3, c: 3 })({ a: 1, b: 2 }), { a: 1}); + * * @category combinators * @since 2.11.0 */ @@ -588,6 +928,18 @@ export const intersection = (M: Magma) => (second: Record) => ( } /** + * Difference between two `Record`s. + * Takes two `Record`s and produces a `Record` composed by the + * entries of the two inputs, removing the entries with the same + * key in both inputs. + * + * @example + * import { difference } from "fp-ts/Record"; + * + * assert.deepStrictEqual(difference({ a: 1 })({ a: 1, b: 2 }), { b: 2 }); + * assert.deepStrictEqual(difference({ a: 3 })({ a: 1, b: 2 }), { b: 2 }); + * assert.deepStrictEqual(difference({ a: 3, c: 3 })({ a: 1, b: 2 }), { b: 2, c: 3 }); + * * @category combinators * @since 2.11.0 */ @@ -651,6 +1003,17 @@ const _traverseWithIndex = (O: Ord) => ( // ------------------------------------------------------------------------------------- /** + * Given a `Predicate`, it produces a new `Record` keeping only the entries with a + * value that satisfies the provided predicate. + * + * @example + * import { filter } from "fp-ts/Record" + * + * assert.deepStrictEqual(filter((s: string) => s.length < 4)({ a: "foo", b: "bar", c: "verylong" }), { + * a: "foo", + * b: "bar", + * }); + * * @category Filterable * @since 2.0.0 */ @@ -661,12 +1024,40 @@ export const filter: { } = RR.filter /** + * Maps a `Record` with an iterating function that returns an `Option` + * and it keeps only the `Some` values discarding the `None`s. + * + * @example + * import { filterMap } from "fp-ts/Record" + * import { option } from "fp-ts" + * + * const f = (s: string) => s.length < 4 ? option.some(`${s} is short`): option.none + * assert.deepStrictEqual(filterMap(f)({ a: "foo", b: "bar", c: "verylong" }), { + * a: "foo is short", + * b: "bar is short", + * }); + * * @category Filterable * @since 2.0.0 */ export const filterMap: (f: (a: A) => Option) => (fa: Record) => Record = RR.filterMap /** + * Partition a `Record` into two parts according to a `Predicate`. + * + * @example + * import { partition } from "fp-ts/Record" + * + * assert.deepStrictEqual(partition((s: string) => s.length < 4)({ a: "foo", b: "bar", c: "verylong" }), { + * left:{ + * c: "verylong" + * }, + * right: { + * a: "foo", + * b: "bar", + * }, + * }); + * * @category Filterable * @since 2.0.0 */ @@ -679,6 +1070,24 @@ export const partition: { } = RR.partition /** + * Maps a `Record` with a function returning an `Either` and + * partitions the resulting `Record` into `Left`s and `Right`s. + * + * @example + * import { partitionMap } from "fp-ts/Record" + * import { either } from "fp-ts" + * + * const f = (s: string) => (s.length < 4 ? either.right(`${s} is short`) : either.left(`${s} is not short`)); + * assert.deepStrictEqual(partitionMap(f)({ a: "foo", b: "bar", c: "verylong" }), { + * left: { + * c: "verylong is not short", + * }, + * right: { + * a: "foo is short", + * b: "bar is short", + * }, + * }); + * * @category Filterable * @since 2.0.0 */ @@ -687,6 +1096,21 @@ export const partitionMap: ( ) => (fa: Record) => Separated, Record> = RR.partitionMap /** + * Reduces a `Record` passing each value to the iterating function. + * Entries are processed in order, sorted by key according to + * the given `Ord`. + * + * @example + * import { reduce } from "fp-ts/Record"; + * import { Ord } from "fp-ts/string"; + * + * const x = { c: 3, a: "foo", b: false }; + * assert.deepStrictEqual(reduce(Ord)([] as string[], (b, a) => [...b, `-${a}-`])(x), [ + * "-foo-", + * "-false-", + * "-3-", + * ]); + * * @category Foldable * @since 2.0.0 */ @@ -704,6 +1128,20 @@ export function reduce( } /** + * Map and fold a `Record`. + * Map the `Record` passing each value to the iterating function. + * Then fold the results using the provided `Monoid`. + * + * @example + * import { foldMap } from "fp-ts/Record"; + * import { Ord } from "fp-ts/string"; + * import { Monoid } from "fp-ts/Monoid"; + * + * const m: Monoid = { empty: "", concat: (x: string, y: string) => (x ? `${x} -> ${y}` : `${y}`) }; + * const f = (a: number) => `-${a}-`; + * const x = { c: 3, a: 1, b: 2 }; + * assert.deepStrictEqual(foldMap(Ord)(m)(f)(x), "-1- -> -2- -> -3-"); + * * @category Foldable * @since 2.0.0 */ @@ -723,6 +1161,21 @@ export function foldMap( } /** + * Same as `reduce` but entries are processed _from the right_, + * i.e. in reverse order, from the last to the first entry, according to + * the given `Ord`. + * + * @example + * import { reduceRight } from "fp-ts/Record"; + * import { Ord } from "fp-ts/string"; + * + * const x = { c: 3, a: "foo", b: false }; + * assert.deepStrictEqual(reduceRight(Ord)([] as string[], (a, b) => [...b, `-${a}-`])(x), [ + * "-3-", + * "-false-", + * "-foo-", + * ]); + * * @category Foldable * @since 2.0.0 */ @@ -740,12 +1193,43 @@ export function reduceRight( } /** + * Compact a `Record` of `Option`s discarding the `None` values and + * keeping the `Some` values. + * + * @example + * import { compact } from 'fp-ts/Record' + * import { option } from 'fp-ts' + * + * assert.deepStrictEqual(compact({ a: option.some("foo"), b: option.none, c: option.some("bar") }), { + * a: "foo", + * c: "bar", + * }); + * * @category Compactable * @since 2.0.0 */ export const compact: (fa: Record>) => Record = RR.compact /** + * Separate a `Record` of `Either`s into `Left`s and `Right`s. + * + * @example + * import { separate } from 'fp-ts/Record' + * import { either } from 'fp-ts' + * + * assert.deepStrictEqual( + * separate({ a: either.right("foo"), b: either.left("bar"), c: either.right("baz") }), + * { + * right: { + * a: "foo", + * c: "baz", + * }, + * left: { + * b: "bar", + * }, + * } + * ); + * * @category Compactable * @since 2.0.0 */ @@ -775,6 +1259,19 @@ declare module './HKT' { } /** + * Produces a `Show` for a `Record`, given a `Show` for the base type + * (a `Show` produces a human-readable representation of an instance). + * `Record` entries are sorted by key with the provided `Ord`. + * + * @example + * import { getShow } from "fp-ts/Record" + * import { Show } from "fp-ts/Show" + * import { Ord } from "fp-ts/string" + * + * const sNumber: Show = { show: (n: number) => `${n}` }; + * const sRecord: Show> = getShow(Ord)(sNumber); + * assert.deepStrictEqual(sRecord.show({ b: 2, a: 1 }), '{ "a": 1, "b": 2 }'); + * * @category instances * @since 2.0.0 */ @@ -793,20 +1290,35 @@ export function getShow( } /** + * Given an `Eq` for the base type, it produces an `Eq` + * for a `Record` of that base type. + * + * @example + * import { getEq } from "fp-ts/Record"; + * import { string } from "fp-ts"; + * import { Eq } from "fp-ts/Eq"; + * + * const eq: Eq> = getEq(string.Eq); + * assert.deepStrictEqual(eq.equals({ a: "foo" }, { b: "bar" }), false); + * assert.deepStrictEqual(eq.equals({ a: "foo" }, { a: "foo" }), true); + * * @category instances * @since 2.0.0 */ export const getEq: (E: Eq) => Eq> = RR.getEq /** - * Returns a `Monoid` instance for `Record`s given a `Semigroup` instance for their values. + * Returns a `Monoid` instance for `Record`s, given a `Semigroup` + * instance for the base type. + * The `Monoid` makes the union of two `Record`s comining the + * overlapping entries with the provided `Semigroup`. * * @example * import { SemigroupSum } from 'fp-ts/number' * import { getMonoid } from 'fp-ts/Record' * - * const M = getMonoid(SemigroupSum) - * assert.deepStrictEqual(M.concat({ foo: 123 }, { foo: 456 }), { foo: 579 }) + * const M = getMonoid(SemigroupSum); + * assert.deepStrictEqual(M.concat({ foo: 123, bar: 234 }, { foo: 456, baz: 567 }), { foo: 579 , bar: 234, baz: 567 }); * * @category instances * @since 2.0.0 @@ -824,6 +1336,17 @@ export const Functor: Functor1 = { /** * Derivable from `Functor`. + * Takes a value and a `Record` of functions and returns a + * `Record` by applying each function to the input value. + * + * @example + * import { flap } from "fp-ts/Record" + * + * const fab = { x: (n: number) => `${n} times 2`, y: (n: number) => `${n * 2}` }; + * assert.deepStrictEqual(flap(3)(fab), { + * x: "3 times 2", + * y: "6", + * }); * * @category combinators * @since 2.10.0 @@ -843,6 +1366,9 @@ export const FunctorWithIndex: FunctorWithIndex1 = { } /** + * Produces a `Foldable` instance for a `Record`, using the + * provided `Ord` to sort the `Record`'s entries by key. + * * @category instances * @since 2.11.0 */ @@ -854,6 +1380,9 @@ export const getFoldable = (O: Ord): Foldable1 => ({ }) /** + * Produces a `FoldableWithIndex1` instance for a `Record`, using the + * provided `Ord` to sort the `Record`'s entries by key. + * * @category instances * @since 2.11.0 */ @@ -913,6 +1442,9 @@ export const FilterableWithIndex: FilterableWithIndex1 = { } /** + * Produces a `Traversable` instance for a `Record`, using the + * provided `Ord` to sort the `Record`'s entries by key. + * * @category instances * @since 2.11.0 */ @@ -927,6 +1459,9 @@ export const getTraversable = (O: Ord): Traversable1 => ({ }) /** + * Produces a `TraversableWithIndex` instance for a `Record`, using the + * provided `Ord` to sort the `Record`'s entries by key. + * * @category instances * @since 2.11.0 */ @@ -971,6 +1506,19 @@ export const getWitherable = (O: Ord): Witherable1 => { } /** + * Given a `Semigroup` in the base type, it produces a `Semigroup` + * in the `Record` of the base type. + * The resulting `Semigroup` concatenates two `Record`s by + * `union`. + * + * @example + * import { getUnionSemigroup } from "fp-ts/Record" + * import { Semigroup } from "fp-ts/Semigroup" + * + * const sNumber: Semigroup = { concat: (x, y) => x - y }; + * const sRecord: Semigroup> = getUnionSemigroup(sNumber); + * assert.deepStrictEqual(sRecord.concat({ a: 1, b: 2 }, { b: 3, c: 4 }), { a: 1, b: -1, c: 4 }); + * * @category instances * @since 2.11.0 */ @@ -982,6 +1530,19 @@ export const getUnionSemigroup = (S: Semigroup): Semigroup(S: Semigroup): Monoid> => }) /** + * Given a `Semigroup` in the base type, it produces a `Semigroup` + * in the `Record` of the base type. + * The resulting `Semigroup` concatenates two `Record`s by + * `intersection`. + * + * @example + * import { getIntersectionSemigroup } from "fp-ts/Record" + * import { Semigroup } from "fp-ts/Semigroup" + * + * const sNumber: Semigroup = { concat: (x, y) => x - y }; + * const sRecord: Semigroup> = getIntersectionSemigroup(sNumber); + * assert.deepStrictEqual(sRecord.concat({ a: 1, b: 2 }, { b: 3, c: 4 }), { b: -1 }); + * * @category instances * @since 2.11.0 */ @@ -1002,6 +1576,19 @@ export const getIntersectionSemigroup = (S: Semigroup): Semigroup> = getDifferenceMagma(); + * assert.deepStrictEqual(m.concat(r1, r2), difference(r2)(r1)); + * assert.deepStrictEqual(m.concat(r1, r2), { c: 3, b: 2 }); + * * @category instances * @since 2.11.0 */