Skip to content

alfonsogoberjr/goated

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

37 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Goated

a currying library for goats 🐐

greatest library of all time!


Curry Goat, best dish on the planet

This library was inspired by Ramda, with a similar goal in mind - completely point-free, curly-bracket-less Javascript code.

The key differences between Goated and Ramda are thus:

  • Types are not mangled. Goated is built by and for Typescript engineers. No more as unknown as any and other such nonsense just to pass tslint.
  • Methods are stripped down to array/collection basics, plus some extras like pipe and compose. The fantasy-land spec is nice but kind of a distraction. We just want pure functional code.
  • Errors are actually useful and stack traces are accurate when debugging.

Install

npm i goated

or

yarn add goated

Then, import:

import { map, pipe, keys, filter, /* etc */ } from 'goated'

API

Quick Reference

add

Adds two values.

See also subtract.

add(2, 3); //  5
add(7)(10); // 17

all

Returns true if all elements of the list match the predicate, false if there are any that don't.

If the list contains Promises, dispatches to Bluebird.all()

See also any.

const equals3 = equals(3);
all(equals3)([3, 3, 3, 3]); // true
all(equals3)([3, 3, 1, 3]); // false

all([
  Promise.resolve({}),
  Promise.resolve(3),
  Promise.resolve(true),
]).then((res) => console.log(res)) // [{}, 3, true]

always

Returns a function that always returns the given value. Note that for non-primitives the value returned is a reference to the original value.

const foo = always('foo')
foo() // 'foo'

and

Returns true if both arguments are true; false otherwise.

See also or.

and(true, true); // true
and(true, false); // false
and(false, true); // false
and(false, false); // false

any

Returns true if at least one of the elements of the list match the predicate, false otherwise.

any([0, false, true]) // true
any([NaN, 0, false]) // false

append

Returns a new list containing the contents of the given list, followed by the given element.

append('tests', ['write', 'more']) // ['write', 'more', 'tests']

apply

Applies function fn to the argument list args. This is useful for creating a fixed-arity function from a variadic function. fn should be a bound function if context is significant.

const nums = [1, 2, 3, -99, 42, 6, 7];
apply(Math.max, nums); // 42

compose

Performs right-to-left function composition. The last argument may have any arity; the remaining arguments must be unary.

See also pipe

const negate = (num) => -num
const powThenNegate = compose<number, number>(negate, Math.pow) // Works with bound functions and preserves context

powThenNegate(3, 3) // -27

Note that the first argument passed to compose, if it's a goated method where currying is optional (like map), its type must be explicitly set using either as or <>, like so:

import { Curried } from 'goated'

const double = map((num: number) => num * 2)
const add = (a, b) => a + b
const doubleThenAdd = compose<number[], number>(<Curried<number, number>>reduce(add, 0), double)
// Necessary because map() returns either a curried fn or a list.

doubleThenAdd([1, 2, 3]) // 12

Also works with async / await! Returned value must be awaited or its then handler called. Performs auto-detection of a Promise/Thenable in the arguments, converting all results to a Promise upon detection.

import { Curried } from 'goated'

type Foo = { foo: number }
const double = map((num: number) => num * 2);
const add = (a, b) => a + b;
const fetch = async (num) => ({ foo: num })

const doubleThenAddFetch = compose<number[], Promise<Foo>>(
  fetch,
  reduce(add, 0),
  <Curried<number, number>>double
);

async () => {
  await doubleThenAddFetch([1, 2, 3]) // { foo: 12 }
}

// or

doubleThenAddFetch([1, 2, 3])
  .then((obj: Foo) => {
    // obj = { foo: 12 }
  })

concat

Returns the result of concatenating the given lists or strings.

Note: concat expects both arguments to be of the same type, unlike the native Array.prototype.concat method. It will throw an error if you concat an Array with a non-Array value.

concat([1, 2], [3, 4, 5]) // [1, 2, 3, 4, 5]

const concatOneTwo = concat([1, 2])

concatOneTwo([3, 4, 5]) // [1, 2, 3, 4, 5]

cond

Returns a function, fn, which encapsulates if/else, if/else, ... logic. cond takes a list of [predicate, transformer] pairs. All of the arguments to fn are applied to each of the predicates in turn until one returns a "truthy" value, at which point fn returns the result of applying its arguments to the corresponding transformer. If none of the predicates matches, fn returns undefined.

const fn = cond([
  [<Curried<number, boolean>>equals(0), always("water freezes at 0Β°C")],
  [<Curried<number, boolean>>equals(100), always("water boils at 100Β°C")],
  [T, (temp) => "nothing special happens at " + temp + "Β°C"],
])

fn(0) // 'water freezes at 0Β°C'
fn(50) // 'nothing special happens at 50Β°C'
fn(100) // 'water boils at 100Β°C'

divide

Divides two numbers. Equivalent to a / b.

See also multiply.

divide(71, 100); // 0.71

const reciprocal = divide(1);
reciprocal(4);   // 0.25

divideBy

Divides the second number by the first. Equivalent to b / a.

See also multiply.

divideBy(4, 40) // 10

const half = divideBy(2);
half(42); // 21

equals

Returns true if its arguments are equivalent, false otherwise. Handles cyclical data structures.

Dispatches to lodash.isEqual. No need to reinvent the wheel.

Unlike R.equals, equals can be curried.

equals(1, 1); // true
equals(1, '1'); // false
equals([1, 2, 3], [1, 2, 3]); // true

const a = {}; a.v = a;
const b = {}; b.v = b;
equals(a, b); // true

const equalsA = equals(a)

equalsA(b) // true

F

A function that always returns false. Any passed in parameters are ignored.

See also T

F() // false

filter

Takes a predicate and a Filterable, and returns a new filterable of the same type containing the members of the given filterable which satisfy the given predicate. Filterable objects include plain objects or any object that has a filter method such as Array.

Dispatches to the filter method of the second argument, if present.

type Foo = { foo: number; bar: number; baz: number };

const onlyOdd = (item: number) => item % 2 === 1

const array = [1, 2, 3]

filter<number>(onlyOdd, array) // [1, 3]

const obj = { foo: 1, bar: 2, baz: 3 }

filter<number>(onlyOdd, obj) // { foo: 1, baz: 3 }

const filterOnlyOdd = <Curried<Foo, number>>filter<number>(onlyOdd);

filterOnlyOdd(obj) // { foo: 1, baz: 3 }

find

Returns the first element of the list which matches the predicate, or undefined if no element matches.

const xs = [{ a: 1 }, { a: 2 }, { a: 3 }];
find(propEq('a', 2))(xs); // { a: 2 }
find(propEq('a', 4))(xs); // undefined

groupBy

Splits a list into sub-lists stored in an object, based on the result of either calling a String-returning function on each element or evaluating the selector key of each element, and grouping the results according to values returned.

Dispatches to the groupBy method of the second argument, if present.

type ObjType = { a: string }
const groupByFn = groupBy<ObjType>((item: ObjType) => (item.a === 'b') ? 'foo' : 'bar')

groupByFn([{ a: 'b' }, { a: 'd' }]) // { foo: [{ a: 'b' }], bar: [{ a: 'd' }] }

const groupByKey = groupBy<ObjType>('a')

groupByKey([{ a: 'b' }, { a: 'd' }]) // { b: [{ a: 'b' }], d: [{ a: 'd' }] }

head

Returns the first element of the given list or string.

head(['fi', 'fo', 'fum']); // 'fi'
head([]); // undefined

head('abc'); // 'a'
head(''); // ''

identity

A function that does nothing but return the parameter supplied to it. Good as a default or placeholder function.

identity(1) // 1

const obj = {};

identity(obj) === obj // true

is

See if an object (val) is an instance of the supplied constructor. This function will check up the inheritance chain, if any.

is(Object, {}); // true
is(Number, 1); // true
is(Object, 1); // false
is(String, 's'); // true
is(String, new String('')); // true
is(Object, new String('')); // true
is(Object, 's'); // false
is(Number, {}); // false

isNil

Checks if the input value is null or undefined.

isNil(null); // true
isNil(undefined); // true
isNil(0); // false
isNil([]); // false

join

Returns a string made by inserting the separator between each element and concatenating all the elements into a single string.

See also split

const spacer = join(' ');
spacer(['a', 2, 3.4]);   // 'a 2 3.4'
join('|', [1, 2, 3]);    // '1|2|3'

keys

Returns a list containing the names of all the enumerable own properties of the supplied object. Note that the order of the output array is not guaranteed to be consistent across different JS platforms.

type Foo = { a: number; b: number; c: number; }
const obj: Foo = { a: 1, b: 2, c: 3 }

keys<Foo>({ a: 1, b: 2, c: 3 }); // ['a', 'b', 'c']

map

Takes a function and a functor, applies the function to each of the functor's values, and returns a functor of the same shape.

Goated provides suitable map implementations for Array and Object, so this function may be applied to [1, 2, 3] or {x: 1, y: 2, z: 3}.

Dispatches to the map method of the second argument, if present.

Note that unlike R.map(), indexes are included by default.

const array = [1, 2, 3]
const obj = { 'foo': 1, 'bar': 2, 'baz': 3 }

const double = (item: number) => item * 2

map<number, number>(double, array) // [2, 4, 6]
map<number, number>(double, obj) // { 'foo': 2, 'bar': 4, 'baz': 6 }

multiply

Multiplies two numbers. Equivalent to a * b but curried.

See also divide.

import { multiply } from 'goated'

const double = multiply(2);
const triple = multiply(3);
double(3);       //  6
triple(4);       // 12
multiply(2, 5);  // 10

not

A function that returns the ! of its argument. It will return true when passed a falsy value, and false when passed a truthy one.

not(true); // false
not(false); // true
not(0); // true
not(1); // false

omit

Returns a partial copy of an object omitting the keys specified.

omit(['a', 'd'], { a: 1, b: 2, c: 3, d: 4 }); // { b: 2, c: 3 }

const omitAD = omit(['a', 'd'])

omitAD({ a: 1, b: 2, c: 3, d: 4 }) // { b: 2, c: 3 }

or

Returns true if one or both of its arguments are true. Returns false if both arguments are false.

See also and.

or(true, true); // true
or(true, false); // true
or(false, true); // true
or(false, false); // false

path

Retrieve the value at a given path.

type Foo = { foo?: { bar?: number; baz: number } };

const obj: Foo = { foo: { bar: 2, baz: 3 } };

path<Foo>(["foo", "bar"], obj) // 2

const getFooBar = path<Foo>(["foo", "bar"])

getFooBar(obj) // 2

pathEq

Determines whether a nested path on an object has a specific value, in equals terms. Most likely used to filter a list.

import { Curried } from "goated"

type Foo = { foo?: { bar?: number; baz: number } };

const obj: Foo = { foo: { bar: 2, baz: 3 } };

pathEq<Foo>(["foo", "bar"], 2, obj) // true

const obj1: Foo = { foo: { baz: 3 } };

const isFooBaz3 = <Curried<Foo, boolean>>pathEq<Foo>(["foo", "baz"], 4)

isFooBaz3(obj1) // false

pathOr

If the given, non-null object has a value at the given path, returns the value at that path. Otherwise returns the provided default value.

type Foo = { foo?: { bar?: number; baz: number } };

const obj: Foo = { foo: { bar: 2, baz: 3 } };

pathOr<Foo>(4, ["foo", "bar"], obj) // 2

const missingBar: Foo = { foo: { baz: 3 } };

pathOr<Foo>(4, ["foo", "bar"], missingBar) // 4

pick

Returns a partial copy of an object containing only the keys specified. If the key does not exist, the property is ignored.

pick(['a', 'd'], { a: 1, b: 2, c: 3, d: 4 }); // { a: 1, d: 4 }
pick(['a', 'e', 'f'])({ a: 1, b: 2, c: 3, d: 4 }); // { a: 1 }

pipe

Performs left-to-right function composition. The first argument may have any arity; the remaining arguments must be unary.

See also compose

const negate = (num) => -num
const powThenNegate = pipe<number, number>(Math.pow, negate) // Works with bound functions and preserves context

powThenNegate(3, 3) // -27

Note that the first argument passed to pipe, if it's a goated method where currying is optional (like map), its type must be explicitly set using either as or <>, like so:

import { Curried } from 'goated'

const double = map((num: number) => num * 2)
const add = (a, b) => a + b
const doubleThenAdd = pipe<number[], number>(<Curried<number, number>>double, reduce(add, 0))
// Necessary because map() returns either a curried fn or a list.

doubleThenAdd([1, 2, 3]) // 12

Also works with async / await! Returned value must be awaited or its then handler called. Performs auto-detection of a Promise/Thenable in the arguments, converting all results to a Promise upon detection.

import { Curried } from 'goated'

type Foo = { foo: number }
const double = map((num: number) => num * 2);
const add = (a, b) => a + b;
const fetch = async (num) => ({ foo: num })

const doubleThenAddFetch = pipe<number[], Promise<Foo>>(
  <Curried<number, number>>double,
  reduce(add, 0),
  fetch
);

async () => {
  await doubleThenAddFetch([1, 2, 3]) // { foo: 12 }
}

prop

Returns a function that when supplied an object returns the indicated property of that object, if it exists.

prop('x', { x: 100 }); // 100
prop('x', {}); // undefined
prop(0, [100]); // 100

propEq

Returns true if the specified object property is equal, in equals terms, to the given value; false otherwise.

import { filter, propEq } from 'goated'

type Kid = { name: string; age: number; hair: string; }

const abby = {name: 'Abby', age: 7, hair: 'blond'};
const fred = {name: 'Fred', age: 12, hair: 'brown'};
const rusty = {name: 'Rusty', age: 10, hair: 'brown'};
const alois = {name: 'Alois', age: 15, disposition: 'surly'};
const kids = [abby, fred, rusty, alois];
const hasBrownHair = propEq('hair', 'brown');

filter(<Curried<Kid, boolean>>hasBrownHair, kids); // [fred, rusty]

propOr

If the given, non-null object has an own property with the specified name, returns the value of that property. Otherwise returns the provided default value.

const alice = {
  name: 'ALICE',
  age: 101
};
const favorite = prop('favoriteLibrary');
const favoriteWithDefault = propOr('Goated', 'favoriteLibrary');

favorite(alice);  //=> undefined
favoriteWithDefault(alice);  // 'Goated'

reduce

Returns a single item by iterating through the list, successively calling the iterator function and passing it an accumulator value and the current value from the array, and then passing the result to the next call.

The iterator function receives two values: (acc, value).

const array = [1, 2, 3]
const add = (a, b) => a + b

reduce<number, number>(add, 0, array) // 6

reject

The complement of filter.

const rejectOdd = (item: number) => item % 2 === 1

const array = [1, 2, 3]

reject<number>(rejectOdd, array) // [2]

const obj = { foo: 1, bar: 2, baz: 3 }

reject<number>(rejectOdd, obj) // { bar: 2 }

reverse

Returns a new list or string with the elements or characters in reverse order.

reverse([1, 2, 3]);  // [3, 2, 1]

subtract

Subtracts its second argument from its first argument.

See also add.

subtract(10, 8) // 2

split

Splits a string into an array of strings based on the given separator.

See also join.

const pathComponents = split('/');
tail(pathComponents('/usr/local/bin/node')); // ['usr', 'local', 'bin', 'node']

split('.', 'a.b.c.xyz.d'); // ['a', 'b', 'c', 'xyz', 'd']

T

A function that always returns true. Any passed in parameters are ignored.

See also F

T() // true

tail

Returns all but the first element of the given list or string (or object with a tail method).

See also head.

tail([1, 2, 3])  // [2, 3]

take

Returns the first n elements of the given list or string (or object with a take method).

take<string>(2, ['foo', 'bar', 'baz']) // ['foo', 'bar']

toAsync

Returns the function provided wrapped in an async function. Note that this is the same as wrapping it in a Promise.

const array = [1, 2, 3];
const double = (item: number) => item * 2;
const doubleAsync = toAsync(double); // Returns async () => double

// works with goated.map()
async () => {
  await all(<Promise<number>[]>map(doubleAsync, array)) // [2, 4, 6]
}

useWith

Accepts a function fn and a list of transformer functions and returns a new curried function. When the new function is invoked, it calls the function fn with parameters consisting of the result of calling each supplied handler on successive arguments to the new function.

If more arguments are passed to the returned function than transformer functions, those arguments are passed directly to fn as additional parameters. If you expect additional arguments that don't need to be transformed, although you can ignore them, it's best to pass an identity function so that the new function reports the correct arity.

const fn = useWith<number, number>(Math.pow, [identity, identity]);

fn(3, 4) // 81
import { Curried } from 'goated'

const fn = useWith<number, Curried<number, number>>(Math.pow, [identity, identity]);

const fn3 = fn(3)

fn3(4) // 81
import { Curried } from 'goated'

const fn = useWith<number, Curried<number, number>>(Math.pow, [identity, identity]);

const fn3 = fn(3)

fn3(4, 5) // calls Math.pow(3, 4, 5). Output is still 81

values

Returns a list of all the enumerable own properties of the supplied object. Note that the order of the output array is not guaranteed across different JS platforms.

values({ a: 1, b: 2, c: 3 }); // [1, 2, 3]

when

Tests the final argument by passing it to the given predicate function. If the predicate is satisfied, the function will return the result of calling the whenTrueFn function with the same argument. If the predicate is not satisfied, the argument is returned as is.

const isFoo = (input) => input === 'foo'
const doBar = (input) => 'bar'

const barWhenFoo = when(isFoo, doBar)

barWhenFoo('foo') // 'bar'

About

a currying library for goats 🐐

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published