Last active
June 9, 2022 09:16
-
-
Save ElectricCoffee/82d2ac727227bc7f4c640cfc05cbbaaa to your computer and use it in GitHub Desktop.
An implementation of Haskell's arrow functions in Typescript. Originally used to deal with key-value pairs, hence the specific names, but the functions are general enough to be used for any two-element arrays.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Turns a single value into a 2 element array, with the same item in both cells | |
* @param a | |
* @returns | |
*/ | |
export const split = <A>(a: A) => [a, a]; | |
/** | |
* Gets the key in a key-value pair | |
* @param param0 a key-value pair. A 2 element array | |
* @returns | |
*/ | |
export const getKey = <A, B>([a, _]: [A, B]) => a; | |
/** | |
* Gets the value in a key-value pair | |
* @param param0 a key-value pair. A 2 element array | |
* @returns | |
*/ | |
export const getValue = <A, B>([_, b]: [A, B]) => b; | |
/** | |
* `mapKey(f)(arr)` updates the left element of a 2 element array. | |
* `mapKey(x => x + 1)([1, 2]) == [2, 2]` | |
* | |
* The reason the function is split, is so it's nicer to use in a map: | |
* ``` | |
* Object.entries(obj) | |
* .map(mapKey(encodeURIComponent)); | |
* ``` | |
* This encodes every key in a key-value list to be URL safe. | |
* @param f a function that takes a single argument and returns anything | |
* @returns | |
*/ | |
export const mapKey = | |
<A1, A2>(f: (a: A1) => A2) => | |
<B>([x, y]: [A1, B]): [A2, B] => | |
[f(x), y]; | |
/** | |
* Same as `mapKey` except "more procedural" looking, for when you don't want to look at `foo(x)(y)`. | |
* Updates the left element of a 2 element array. | |
* @param kvp | |
* @param f | |
* @returns | |
*/ | |
export const mapKey2 = <A1, A2, B>(kvp: [A1, B], f: (a: A1) => A2) => | |
mapKey(f)(kvp); | |
/** | |
* `mapValue(f)(arr)` updates the right element of a 2 element array. | |
* `mapValue(x => x + 1)([1, 2]) == [1, 3]` | |
* | |
* The reason the function is split, is so it's nicer to use in a map: | |
* ``` | |
* Object.entries(obj) | |
* .map(mapValue(encodeURIComponent)); | |
* ``` | |
* This encodes every value in a key-value list to be URL safe. | |
* @param f a function that takes a single argument and returns anything | |
* @returns | |
*/ | |
export const mapValue = | |
<B1, B2>(f: (b: B1) => B2) => | |
<A>([x, y]: [A, B1]): [A, B2] => | |
[x, f(y)]; | |
/** | |
* Same as `mapValue` except "more procedural" looking, for when you don't want to look at `foo(x)(y)`. | |
* Updates the right element of a 2 element array. | |
* @param kvp | |
* @param f | |
* @returns | |
*/ | |
export const mapValue2 = <A, B1, B2>(kvp: [A, B1], f: (b: B1) => B2) => | |
mapValue(f)(kvp); | |
/** | |
* `mapBoth(f)(arr)` updates both elements of a 2 element array, so long as they're both the same type. | |
* `mapBoth(x => x + 1)([1, 2]) == [2, 3]` | |
* | |
* The reason the function is split, is so it's nicer to use in a map: | |
* ``` | |
* Object.entries(obj) | |
* .map(mapBoth(encodeURIComponent)); | |
* ``` | |
* This encodes both the key and the value in a key-value list to be URL safe. | |
* @param f a function that takes a single argument and returns anything. Note that `f` is applied to both values in the array. | |
* @returns | |
*/ | |
export const mapBoth = | |
<T, R>(f: (x: T) => R) => | |
([x, y]: [T, T]): [R, R] => | |
[f(x), f(y)]; | |
/** | |
* Same as `mapBoth` except "more procedural" looking, for when you don't want to look at `foo(x)(y)`. | |
* Updates both elements in a 2 element array. | |
* @param kvp | |
* @param f | |
* @returns | |
*/ | |
export const mapBoth2 = <T, R>(kvp: [T, T], f: (x: T) => R) => mapBoth(f)(kvp); | |
/** | |
* `joinPair(f)(arr)` combines a 2 element array into a single item. | |
* `joinPair((x, y) => x + y)([1, 2]) == 3` | |
* | |
* The reason the function is split, is so it's nicer to use in a map: | |
* ``` | |
* Object.entries(obj). | |
* .map(joinPair((k, v) => `${k}: ${v}`)); | |
* ``` | |
* This example creates a list of strings of the shape "attribute: value". | |
* @param f a function that takes two arguments and returns anything | |
* @returns | |
*/ | |
export const joinPair = | |
<A, B, C>(f: (a: A, b: B) => C) => | |
([x, y]: [A, B]) => | |
f(x, y); | |
/** | |
* Same as `joinPair` except "more procedural" looking, for when you don't want to look at `foo(x)(y)`. | |
* Combines both elements in a 2 element array into a single value. | |
* @param kvp | |
* @param f | |
* @returns | |
*/ | |
export const joinPair2 = <A, B, C>(kvp: [A, B], f: (a: A, b: B) => C) => | |
joinPair(f)(kvp); | |
/** | |
* Takes an object and transforms every entry in it according to some function. | |
* ``` | |
* transformObject(mapKey(x => x.toUpperCase()))({foo: 3, bar: 4}) === {FOO: 3, BAR: 4} | |
* ``` | |
* The parameters are separated to make it work nicer in a map: | |
* ``` | |
* myListOfObjects.map(transformObject(mapValue(x => x + 3))); | |
* ``` | |
* This adds 3 to every value on every attribute in an object. | |
* @param f a function that takes a key-value pair and returns a key-value pair | |
* @returns | |
*/ | |
export const transformObject = | |
(f: (arr: [string, any]) => [string, any]) => | |
(obj: object): object => { | |
const entries = Object.entries(obj); | |
const updated = entries.map(f); | |
return Object.fromEntries(updated); | |
}; | |
/** | |
* Same as `transformObject` except "more procedural" looking, for when you don't want to look at `foo(x)(y)`. | |
* Transforms every entry in an object. | |
* @param obj | |
* @param f | |
* @returns | |
*/ | |
export const transformObject2 = ( | |
obj: object, | |
f: (arr: [string, any]) => [string, any] | |
) => transformObject(f)(obj); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment