Skip to content

Instantly share code, notes, and snippets.

@ElectricCoffee
Last active June 9, 2022 09:16
Show Gist options
  • Save ElectricCoffee/82d2ac727227bc7f4c640cfc05cbbaaa to your computer and use it in GitHub Desktop.
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.
/**
* 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