# Learning Rambda(x)

Published January 22nd, 2020 20m read time I've been getting into functional programming as a of late, mostly due to having watched Kyle Simpson's course on Frontend Masters. I looked at a bunch of FP libraries, but the one I settled on was Rambda. It maintains most of Ramda's API but cuts out a lot of the possibly more unnecessary stuff. This single blog post is my chronicle of learning Rambda along with its extended version, Rambdax.

``````add(a: number, b: number): number
subtract(a: number, b: number): number``````

Well, this is an easy one to get started with. The first time I saw `add` as a function, I thought it was dumb, but once you realize you can do this:

``````const add5 = R.add(5)
const result = add5(3) // 8``````

The power of functional programming really starts to shine.

``adjust(i: number, replaceFn: Function, arr: T[]): T[]``

This isn't a function I think I'd use a ton right away, but I can see how it could be useful someday.

``````R.adjust(1, v v.toUpperCase(), ['brandon', 'lee', 'pittman'])
// [ 'brandon', 'LEE', 'pittman' ]``````

Could be useful in a loop where you needed to act on each item in the array, but then why not just use `map` and create a new array?

## all

``all(fn: Function, arr: T[]): boolean``

This one reminds me of a `filter`-style function. Again, probably not useful on its own, but useful when combined with other utilities.

``R.all(v v.length == 3, ['Bob', 'Tim', 'Sam'])``

## allPass

``allPass(rules: Function[], input: any): boolean``

First, set up rules like:

``````var rules = {
x x > 30
}

var over30 = R.allPass(rules)``````

Then pass an object to see if it passes.

``````var brandon = { age: 36 }
var lisa = { age: 26 }
over30(brandon) // true
over30(lisa) // false

// Combine with all for fun and profit
var allOver30 = R.all(over30)
allOver30([brandon]) // true
allOver30([brandon, lisa]) // false``````

## always/indentiy

``````always(x: any): Function
identity(x: T): T``````

Well, you just get a function that returns the thing you originaly gave it or the thing itself.

``````const brandon = R.always('Brandon')
brandon() // 'Brandon``````

## and

It's like `&&`.

``````R.and(1 > 0, 2 > 1) // true
R.and(1 > 0, 0 > 1) // false``````

## any

``any(condition: Function, arr: T[]): boolean``

Does what it says on the tin.

``````var greaterThan100 = x x > 100
R.any(greaterThan100, [99,100,110]) // true``````

## anyPass

The any version of `allPass`.

## append/prepend

``````append(valueToAppend: T, arr: T[]): T[]
prepend(x: T, arr: T[]): T[]``````

`push` and `unshift` for the FP kids.

## assoc

``assoc(prop: any, value: any, obj: object): object``

Sets a key/value on an object. Can be broken up for flexibility.

``````var person = {name: "John"}
var setAge = R.assoc('age')
var setAgeTo20 = setAge(20)
setAgeTo20(person) // {name: "John", age: 20}``````

## both/either

``````both(firstCondition: Function, secondCondition: Function, input: any): boolean
either(firstCondition: Function, secondCondition: Function): Function``````

Like `allPass` and `anyPass`, but for two functions only. Pass functions as positional parameters instead of as an array.

``R.either(v R.startsWith('B', v), v R.endsWith('n', v))('Lenny') // false``

## clone

``clone(objOrArr: T|T[]): T|T[]``

Let's clone some shit, boys! Deep cloning, to boot.

## compose/pipe

``````compose(fn1: Function, ... , fnN: Function): any
pipe(fn1: Function, ... , fnN: Function): any``````

Combine functions into Megatron.

## complement/opposite

``````complement(fn: Function): Function
opposite(fn: Function): Function``````

If the supplied function equals `true`, return `false`, and vice versa. Well that's fun.

``````const fn = (name name == 'Brandon')
fn('Brandon') // false
const fn('Sammy') // true``````

## concat

Just an FP version of the normal `Array.concat()`.

## curry

``curry(fn: Function): Function``

Finally! On to the serious FP stuff. Combine functions into their full FP glory.

``````const addFourNumbers = (a, b, c, d) a + b + c + d
const g = f(3)
const result = g(4) // 10``````

## inc/dec

``````R.inc(2) // 2
R.dec(2) // 1``````

`++` and `--` for FP folks.

## defaultTo

``defaultTo(defaultValue: T, ...inputArguments: any[]): T``

Lets you set a default value. Great for when you need a fallback.

``````const defaultToBrandon = R.defaultTo('Brandon')
var name
defaultToBrandon(name) // 'Brandon'
name = "Bruce Wayne"
defaultToBrandon(name) // 'Bruce Wayne'``````

## dissoc

``dissoc(prop: any, obj: object): object``

Returns an object without the specified key.

``````var person = { name: 'Brandon', age: 36 }
const removeAge = R.dissoc('age')
removeAge(person) // { name: 'Brandon' }``````

## divide

``R.divide(100,5) // 20``

## drop/dropLast/take

``````drop(howManyToDrop: number, arrOrStr: T[]|string): T[]|String
dropLast(howManyToDrop: number, arrOrStr: T[]|String): T[]|String
take(num: number, arrOrStr: T[]|string): T[]|string
takeLast(num: number, arrOrStr: T[]|string): T[]|string``````

Removes the indicated number of items or characters from the provided array or string.

``````var numbers = [1,2,3]
R.drop(2, numbers) // 
R.dropLast(2, numbers) // ``````

## startsWith/endsWith

``````startsWith(x: string, str: string): boolean
endsWith(x: string, str: string): boolean``````

Checks if a string starts or ends with a provided string. Regex not allowed.

``````var name = 'Brandon'
R.startsWith('B', name) // true
R.endsWith('n', name) // true``````

## equals

``equals(a: any, b: any): boolean``

Checks equality even in objects.

``````R.equals(
[1, {a:2}, [{b:3}]],
[1, {a:2}, [{b:3}]]
) // true``````

## F/T

Always returns `false` or `true`.

## filter

``filter(filterFn: Function, x: Array|Object): Array|Object``

`filter`, but with the parameters reversed.

``````const filterBrandon = R.filter(v v.toLowerCase() != 'brandon')
filterBrandon(['Brandon']) // []
filterBrandon(['Brandon', 'Lisa']) // ['Lisa']``````

## find/findIndex/findLast/findLastIndex

``````find(findFn: Function, arr: T[]): T|undefined
findIndex(findFn: Function, arr: T[]): number
findLast(findFn: Function, arr: T[]): T|undefined
findLastIndex(findFn: Function, arr: T[]): number``````

`find`, but with the parameters reversed. `findIndex` will return `-1` if nothing can be matched. `findLast` is the inverse of `find`. `findLastIndex` is the inverse of `findIndex`.

``````var fn = v v > 5
R.find(fn, [1,2,3,4,6]) // 6``````

## flatten

``flatten(arr: any[]): any[]``

Like a pancake, yo.

``R.flatten([ 1, [ 2, [ 3 ] ] ]) // [ 1, 2, 3 ]``

## flip

``flip(fn: Function): Function``

Returns a copy of the provided function with its parameters in reverse.

``````const subtractFlip = R.flip(R.subtract)
const result = subtractFlip(1,7)// 6``````

## forEach

``forEach(fn: Function, x: Array|Object): Array|Object``

Good ol' forEach for FP folks.

## fromPairs

``fromPairs(list: any[]): object``

Equivalent to `Object.fromEntries`.

## groupBy

``groupBy(fn: Function, arr: Array): Object``

Takes the supplied function to test array items and assign to generated key.

``````people = [{first: 'Brandon', last: 'Pittman'}, {first: 'Lisa', last: 'Jones'}]
R.groupBy(x x.last.toLowerCase(), people)
// {
//  Pittman: [ { first: 'Brandon', last: 'Pittman' } ],
//  Jones: [ { first: 'Lisa', last: 'Jones' } ]
// }``````

## groupWith

``groupWith(fn: Function, arr: Array): Array``

Takes the supplied function to test array items and group them when they satisfy the predicate function.

## has

``has(prop: string, obj: Object): boolean``

Check is an object has a particular property.

``R.has('name', {name: 'Brandon'}) // true``

``````head(arrOrStr: T[]|string): T|string
tail(arrOrStr: T[]|string): T[]|string``````

Head to tail carnivores favorite functions.

## identical

``identical(a: any, b: any): boolean``

Is we both the same ting?

## ifElse

``ifElse(condition: Function|boolean, ifFn: Function, elseFn: Function): Function``

Evaluates second fn if first fn is true, else it evaluates the third fn.

``````var fn = R.ifElse(
x x.toLowerCase() == 'brandon',
_ 'Cool guy',
_ 'Not as cool'
)
fn('Brandon') // "Cool guy"``````

## includes

``includes(valueToFind: T|string, input: T[]|string): boolean``

Next level `includes`.

``````R.includes('oo', 'foo') // true
R.includes({a: 1}, [{a: 1}]) // true``````

## indexBy

``indexBy(condition: Function|String, arr: T[]): Object``

Generates an object with items from array that match the provided function or object path string.

``````const arr = [ {id: 1}, {id: 2} ]
const result = R.indexBy(
x x.id,
arr
)
const pathResult = R.indexBy(
'id',
arr
)
// { 1: {id: 1}, 2: {id: 2} }``````

## indexOf/lastIndexOf

``````indexOf(valueToFind: any, arr: T[]): number
lastIndexOf(x: any, arr: T[]): number``````

Like `findIndex` for a specific value, not a function. Returns `-1` if not found.

``````R.indexOf(1, [1, 2]) // 0
R.indexOf(0, [1, 2]) // -1``````

## init

``init(arrOrStr: T[]|string): T[]|string``

Opposite of `tail()`.

``````R.init([1, 2, 3])  // [1, 2]
R.init('foo')  // 'fo'``````

## is/isNil/isEmpty

``````is(xPrototype: any, x: any): boolean
isNil(x: any): boolean
isEmpty(x: any): boolean``````

`is` checks against `instanceof`, but doesn't seem to work with `Array`.

``R.is(String, 'foo') // true``

## join

``oin(separator: string, arr: T[]): string``

Good ol' `join`.

## keys

``keys(x: Object): string[]``

`Object.keys()`

## last

``last(arrOrStr: T[]|string): T|string``

Opposite of `head`.

## length

``length(arrOrStr: Array|String): Number``

The usual `length`.

## map

``map(mapFn: Function, x: Array|Object): Array|Object``

`map` for FP people. Works on `Object`s as well.

## match

``match(regExpression: Regex, str: string): string[]``

`String.match`, yo.

## max/maxBy/min/minBy

``````max(x: Number|String, y: Number|String): Number|String
maxBy(fn: Function, x: Number|String, y: Number|String): Number|String
min(x: Number|String, y: Number|String): Number|String
minBy(fn: Function, x: Number|String, y: Number|String): Number|String``````

Self-explanatory.

## merge/mergeRight

``````merge(a: Object, b: Object)
mergeRight(a: Object, b: Object)``````

`Object.assign`.

## modulo

``modulo(a: number, b: number):numberNumber``

Remainder of a/b.

## multiply

``multiply(a: number, b: number): number``

You know…

## not

``not(x: any): boolean``

`!` as a function.

``````R.not(true) // false

## omit

```typescript
omit(propsToOmit: string[]|string, obj: Object): Object``````

`R.dissoc` for multiple keys.

``R.omit('a,c,d', {a: 1, b: 2, c: 3}) // {b: 2}``

## path

``path(pathToSearch: string[]|string, obj: Object): any``

Grab a value from an object by path string.

``R.path('a.b', {a: {b: 1}}) // 1``

## pathOr

``pathOr(defaultValue: any, pathToSearch: string[]|string, obj: Object): any``

Like `path` but lets you provide a default value, kinda like `defaultTo`.

``````R.pathOr(1, 'a.b', {a: {b: 2}}) // 2
R.pathOr(1, ['a', 'b'], {a: {b: 2}}) // 2
R.pathOr(1, ['a', 'c'], {a: {b: 2}}) // 1``````

## partial

``partial(fn: Function, ...inputs: any[]): Function | any``

Like `curry`, but you can pre-supply arguments.

``````const fn = (salutation, title, firstName, lastName) {
salutation + ', ' + title + ' ' + firstName + ' ' + lastName + '!'
}

const canPassAnyNumberOfArguments = partial(fn, 'Hello', 'Ms.')
const finalFn = canPassAnyNumberOfArguments('foo')

finalFn('bar') //  'Hello, Ms. foo bar!'``````

## partialCurry

``partialCurry(fn: Function|Async, partialInput: Object, input: Object): Function|Promise``

It's like `partial`, but it lets you use an object and its keys instead of positional parameters.

``````var fn = ({first, middle, last}) `\${first} \${middle} \${last}`
var start = R.partialCurry(fn, {first: 'Brandon'})
var final = start({middle: 'Lee', last: 'Pittman'}) // "Brandon Lee Pittman"``````

## pick

``pick(propsToPick: string[], obj: Object): Object``

An grab just what you need from an object.

``````me = {first: 'Brandon', middle: 'Lee', last: 'Pittman'}
R.pick(['first', 'last'], me)
// { first: 'Brandon', last: 'Pittman' }``````

## pluck

``pluck(property: string, arr: Object[]): any[]``

Kinda like pick, but you're pulling specific key off of each object in an array.

``````b = {first: 'Brandon', last: 'Pittman'}
l = {first: 'Lisa', last: 'Jones'}
people = [b,l]
R.pluck('first', people) // [ 'Brandon', 'Lisa' ]``````

## prop

``prop(propToFind: string, obj: Object): any``

`Object[prop]` as a Function.

``````b = {first: 'Brandon', last: 'Pittman'}
R.prop('first', b) // "Brandon"``````

## propEq

``propEq(propToFind: string, valueToMatch: any, obj: Object): boolean``

Checks if the provided prop equals the value given against a provide object.

``````b = {first: 'Brandon', last: 'Pittman'}
R.propEq('first', 'Brandon', b) // true``````

## propIs

``propIs(type: any, name: string, obj: Object): boolean``

Checks type of prop.

``````b = {first: 'Brandon', last: 'Pittman'}
R.propIs(String, 'first', b)``````

## propOr

``propOr(defaultValue: any, param: string, obj: Object): any``

`prop()` but with a fallback.

``R.propOr('Brandon Pittman', 'name', b) // "Brandon Pittman"``

## range

``range(start: number, end: number): number[]``

Returns an array of numbers starting with `start` and going up to `end`.

``R.range(0,3) // [0,1,2]``

## reduce

``reduce(iteratorFn: Function, accumulator: any, array: T[]): any``

Good ol' `reduce`.

## reject

``reject(fn: Function, arr: T[]): T[]``

Opposite of `filter`.

## repeat

``repeat(valueToRepeat: T, num: number): T[]``

Works like Ruby's `#times` method.

``R.repeat('brandon', 2) // ['brandon', 'brandon']``

## replace

``replace(strOrRegex: string|Regex, replacer: string, str: string): string``

`String.replace()`, essentially.

## reverse

``reverse(str: T[]): T[]``

`String.reverse()`, essentially.

## slice

``slice(list: T[], from: Number, to: Number)``

`Array.prototype.slice`!!!

## sort

``sort``

`Array.prototype.sort`!!!

## sortBy

``sortBy``

Sort but can take object props.

## split

``split(separator: string, str: string): string[]``

`String.split()`

## splitEvery

``splitEvery(sliceLength: number, arrOrString: T[]|string): T[T[]]|string[]``

`String.split`, but split by length rather than a specific string.

## sum

``sum(listOfNumbers: number[]): number``

Handy `sum` so you don't need to write your own with `reduce`.

## tap

``tap(fn: Function, input: T): T``

Runs the function but returns the input.

## test

``test(regExpression: Regex, str: string): boolean``

Like an assertion.

## times

``times(fn: Function, n: number): T[]``

Runs the function `n` number of times. Passes the current iteration number to the function.

``R.times(R.identity, 5) // [0, 1, 2, 3, 4]``

## toLower/toUpper

``````toLower(str: string): string
toUpper(str: string): string``````

Saving characters.

## toPairs

``toPairs(obj: object): any[]``

`Object.toEntries()`.

## toString

``toString(x: any): string``

Yup.

## transpose

``transpose(input: Array): Array``

Spreads arrays out across new arrays.

``````const input = [[10, 11], , [], [30, 31, 32]]
const expected = [[10, 20, 30], [11, 31], ]
const result = R.transpose(input)
// result === expected``````

## trim

``trim(str: string): string``

`String.trim()`

## type

``type(a: any): string``

Returns type of argument; is async function and promise aware.

## uniq/uniqWith

``````uniq(arr: T[]): T[]
uniqWith(fn: Function, arr: T[]): T[]``````

Returns an array with just uniq elements. No more Array.from(new Set(...))!

``R.uniq([1, 1, 2, 1]) // [1, 2]``

## uniqWith

``uniqWith(fn: Function, arr: T[]): T[]``

Like `adjust`, without needing a function.

``R.update(0, 'foo', ['bar', 'baz']) // ['foo', baz]``

## values

``values(obj: Object): Array``

`Object.values()`

## without

``without(a: T[], b: T[]): T[]``

Return array minus the supplied values.

``````R.without([1, 2], [1, 2, 3, 4])
// [3, 4]``````

## zip/zipObj

``zip(a: K[], b: V[]): Array``

Unlike `transpose`, `zip` will end after the shortest list is done.

``````R.zip([1, 2, 3, 4], ['A', 'B'])
// [[1, 'A'], [2, 'B']]
R.zipObj(['a', 'b', 'c'], [1, 2])
// {a: 1, b: 2}``````

## Review

Whew! That wraps up all the function is Rambda. A lot of them are utility functions you've used in vanilla JS or with another utility library like Lodash. There aren't a ton of FP-specific functions, but the way they are structured, they are perfect for programming in a functional style. Now, onto the stuff just in Rambdax. Those functions are the more interesting ones as far as I'm concerned.

## {all,any}{False,True,Type}

``````allFalse(...inputs: any|predicate[]): boolean
allTrue(...inputs: any|predicate[]): boolean
allType(targetType: string): (...inputs: any[]) => boolean
anyFalse(...inputs: any|predicate[]): boolean
anyTrue(...inputs: any|predicate[]): boolean
anyType``````

Acts like a filter function for various cases.

## change

``change(origin: object, path: string, changeData: any): object``

Update an object using a path syntax. Will dynamically create sub-keys as needed.

``````var me = {}
R.change(me, 'name.last', 'Pittman')
// { name: { last: 'Pittman' } }``````

## composeAsync/pipedAsync

``````composeAsync(...fns: Array<Function|Async>)(startValue: any): Promise
pipedAsync(input: any, ...fns: Array<Function|Async>): Promise``````

Like `compose`, but supports using async functions.

## composed/piped

``````composed(...fnList: any[]): any
piped(...fnList: any[]): any``````

`compose` but lets you pass the input as the final parameter. `pipe` but lets you pass the input as the first parameter.

``````const result = composed(
R.map(x => x*10),
R.filter(x => x > 1),
[1,2,3]
)
// => [20, 30]``````

## debounce

``debounce(fn: Function, ms: number): any``

Maybe someday I'll remember the difference between this and `throttle`.

Creates a debounced function that delays invoking fn until after wait milliseconds ms have elapsed since the last time the debounced function was invoked.

## defaultToStrict

``defaultToStrict(defaultValue: T, ...inputArguments: any[]): T``

Like the regular `defaultTo`, but ya know, stricter.

``````R.defaultToStrict('foo', undefined) // => 'foo'
R.defaultToStrict('foo', 1) // => 'foo'
R.defaultToStrict('foo', {}) // => 'foo'
R.defaultTo('foo', undefined, 1, [], {}) // => 'foo'
R.defaultTo('foo', undefined, 1, [], {}, 'bar') // => 'bar'``````

## defaultToWhen

``defaultToWhen(fallback: any, fn: Function, ...inputArguments: any[]): any``

Returns the fallback if no argument passes the predicate function.

``````const fn = x => x > 2
const fallback = 10
const result = R.defaultToWhen(fallback, fn, 1,6,8,0 )
// result is 6``````

## delay

``delay(ms: number): Promise``

It's a wait function. Should be used in an async function.

``````const result = R.delay(1000)
// `result` resolves to `'RAMBDAX_DELAY'```````

## filterAsync

``filterAsync(predicate: Async, iterateOver: object|array): Promise``

`filter`, but async.

## findInObject

``findInObject(fn: Function, obj: object): object``

Like `Array.find` but for objects. Only returns the first match.

``````b = { first: "Brandon" }
R.findInObject((v,k) => v.length > 6, b)
// { prop: 'first', value: 'Brandon' }``````

## getter/setter/reset

``````getter(key: undefined|string|string[]): any
setter(key: string|object, value?: any): void
reset(): void``````

Use Rambdax's cache object.

``````R.setter('name', 'Brandon')
R.getter('name') // "Brandon"
R.reset() // everything's undefined now``````

## glue

``glue(input: string, glueString?: string): string``

It's like `join` but for strings.

## hasPath

``hasPath(input: string|string[], input: object): boolean``

Checks to see if the provided path exists on the object.

``headObject(input: object): {prop: string, value: T}``

Takes an object with just one key and returns a new object with a `prop` and `value` set of keys.

``````var double = x => x * 2
R.headObject({double}) // { prop: 'double', value: [Function] }``````

## includesType

``includesType(targetType: string, list: any[]): boolean``

`Array.includes` but checks if a type is the same.

``````const result = R.includesType(
'String',
[1,2,'foo']
) // true``````

## inject

``inject(injection: string, marker: string, str: string, beforeFlag: boolean): string``

Injects the `inject` string after (or before with `true`) the `MARKER` location. Seems like it would be better to replace the marker.

``R.inject('Lee', 'MIDDLE', 'Brandon MIDDLE Pittman', true) // "Brandon LeeMIDDLE Pittman"``

## isAttach

``isAttach(): boolean``

Bolts on an `is` method to object-like variables to let you check type.

``````R.isAttach()
const foo = [1,2,3]
const result = foo.is(['number']) // => true``````

## isFalsy

``isFalsy(x: any): boolean``

Checks if falsy. Oddly, no `isTruthy` to be found.

## isFunction

``isFunction(x: any): boolean``

## isNil

``isNil(x: any): boolean``

`null` or `undefinded`?

## isPromise

``isPromise(x: any): boolean``

## isType

``isType(xType: string, x: any): boolean``

Seems to be the same as `R.is`.

## isValid

``isValid({ input: object: schema: object }): boolean``

Lets you run validation checks against a schema! Simply returns `false` if not valid. Doesn't throw.

Also, superstruct is a thing.

``````// as of 2020-01-28
var brandon = {name: 'Brandon Pittman', married: false}
R.isValid({input: brandon, schema: { name: String, married: Boolean}})``````

## maybe

``maybe(ifRule: Boolean, whenIf: T, whenElse: T): T``

Acts like ternary operator. Recommended when dealing with nested ternary operator usage. Doesn't require the stems to be functions.

``````const x = 4
const y = 8
const result = R.maybe(
x > 2,
y > 10 ? 3 : 7,
5
) // result == 7``````

## mapAsync/mapFastAsync

``````mapAsync(fn: Async|Promise, arr: Array): Promise
mapFastAsync(fn: Async|Promise, arr: Array): Promise``````

Async mapping operations; sequentially or in parallel. Best used with `composeAsync`.

## memoize

``memoize(fn: Function|Promise): any``

Returns cached result of provided function.

## mergeAll

``mergeAll(input: Object[]): Object``

Merges all objects in the array sequentially.

## mergeDeep

``mergeDeep(slave: object, master: object): object``

Deeply merge objects, master object's keys win.

## nextIndex

``nextIndex(index: number, list: any[]): number``

Returns the next index of the list, but loops back to 0 when reaching the end.

## ok

``ok(...inputs: any[]): (schemas: any[]) => true | Error``

`R.isValid`-style checks, but throws error if validation fails.

``````const result = R.ok(
1, [ 'foo', 'bar' ]
)('number', [ 'string' ])
// true``````

## once

``once(fn: Function): Function``

Returns a function that can only ever be invoked once.

## otherwise

``otherwise(fallback: Function, toResolve: Promise): Promise``

Catches and handles errors inside `compose` and `pipe` calls. Returns `identity` if no error occurs.

## pathEq

``pathEq(path:string|string[], target: any, obj: object): boolean``

Checks for equality at `path`.

``````const result = R.pathEq(
'a.b',
1,
{a: {b:1} }
) // true``````

## pass

``pass(...inputs: any[]): (schemas: any[]) => boolean``

Like `R.ok`, but doesn't throw, just returns false.

## partition

``partition(predicate: Function, input: Array|Object): [Array|Object, Array|Object]``

Like `filter`, but returns two arrays — one with passing items and one with rejected items.

## produce

``produce(conditions: Object, input: any): Promise|Object``

Passes `input` to each function in the object.

``````const conditions = {
foo: a => a > 10,
bar: a => ({baz:a})
}

const result = R.produce(conditions, 7)

const expectedResult = {
foo: false,
bar: {baz: 7}
} // result === expectedResult``````

## promiseAllObject

``promiseAllObject(promises: Object): Promise``

Loops over promises in an object.

## random

``random(min: number, max: number): number``

Returns a random number. Duh.

## remove

``remove(inputs: string|RegExp[], text: string): string``

Removes each string in the provided array from the input text.

``````const result = remove(
['foo','bar'],
'foo bar baz foo'
) // 'baz foo'``````

## renameProps

``renameProps(rules: Object, input: Object): Object``

Renames the keys on an object.

``````const rules = {
f: "foo",
b: "bar"
}
const input = {
f:1,
b:2
}
const result = R.renameProps(rules, input)
const expectedResult = {
foo:1,
bar:2
} // result === expectedResult``````

## resolve

``resolve(afterResolve: Function, toResolve: Promise): Promise``

Turns a composition asynchronous.

``````const expected = {
firstName : 'FIRST_NAME_FOO',
lastName  : 'LAST_NAME_FOO',
}

const fetchMember = async x => {
await R.delay(200)

return {
a         : 1,
firstName : `FIRST_NAME_\${ x.query }`,
lastName  : `LAST_NAME_\${ x.query }`,
}
}

const getMemberName = pipe(
email => ({ query : email }),
fetchMember,
resolve(pick('firstName,lastName'))
)
const result = await getMemberName('FOO')
// result === expected``````

## s

``s(): undefined``

Lets you chain function calls.

``````R.s() // true
"foo".s(R.toUpper) // "FOO"``````

## sortObject

``sortObject(predicate: Function, obj: Object): Object``

Allows you to sort an object using its props and values.

``````const predicate = (propA, propB, valueA, valueB) => valueA > valueB ? -1 : 1
const sorted = R.sortObject(predicate, {a:1, b: 4, c: 2})
// {b:4, c: 2, a:1}``````

## shuffle

``shuffle(arr: T[]): T[]``

Just like Icky Woods.

## switcher

A chainable switch statement. Must have a `.default()` call at the end.

``````const valueToMatch = {foo: 1}

const result = R.switcher(valueToMatch)
.is('baz', 'is baz')
.is( x => typeof x === 'boolean', 'is boolean')
.is({foo: 1}, 'Property foo is 1')
.default('is bar')

console.log(result) // => 'Property foo is 1'``````

## tapAsync

``tapAsync(fn: Function|Async|Promise, inputArgument: T): T``

Like `tap`, but accepts an async function.

## template

``template(input: string, templateInput: object): string``

Lets you to Handlebars-style templating with the input providing the values to evaluate.

Currently waiting to see if PR is accepted to allow for blanket JS evaluations.

## toDecimal

``toDecimal(num: number, charsAfterDecimalPoint: number): number``

Convert number to decimal number.

``R.toDecimal(2.45464,2) // 2.45``

## throttle

``throttle(fn: Function, period: number): Function``

Creates a function that will only be called once every `n` milliseconds.

## tryCatch

``tryCatch(fn: Async|Function, fallback: any): Function``

Will return `fallback` if `fn` throws an error.

## unless

``unless(rule: Function|boolean, whenFalse: Function|any): Function``

Inverse of an `if`.

``````const result = R.unless(
R.isNil,
R.inc
)(1) // 2``````

## wait

``wait(fn: Async): Promise<[any, Error]>``

Returns a promise that resolves to an array with a return value and an error, a la Go.

``````void async function wait(){
const [result, err] = await R.wait(R.delay(1000))
// => err is undefined
// => result is `RAMBDAX_DELAY`
}()``````

## waitFor

``waitFor(condition: any, ms: number): Promise``

Gives you a promise that resolves to true if the provided function returns true without the alloted amount of time.

## where

``where(conditions: object, input: object): boolean``

Returns a function that takes an object and validates each prop.

``````const condition = R.where({
a : aProp => typeof aProp === "string",
b : bProp => bProp === 4
})

const result = condition({
a : "foo",
b : 4,
c : 11,
}) // true``````

## whereEq

``whereEq(rule: object, input: any): boolean``

Validates object values against a `rule` object.

## when

``when(rule: Function|boolean, whenTrue: Function|any): Function``

If as a Function™. Passes input value through if it doesn't meet the rule.

``````const truncate = R.when(
x => x.length > 5,
R.compose(x => `\${x}...`, R.take(5))
)

const result = truncate('12345678')
// '12345...'``````

## whenAsync

``whenAsync(rule: condition: Async | Function | boolean, whenFn: Async | Function): Promise``

Async version of `when`.

## Wrap-Up

Well, I did it. I went through all the functions. Did I memorize them all? Probably not. But I learned a great deal about the library and I'm starting to see places in my own code where I could use it more and more. I even made a PR to enhance the `template` function. Sadly, the response time from the maintainer is a little slower than I'd like. I also found a handful of places in the docs where they weren't 100% accurate. Rambda doesn't have the user base that Ramda does, so it's natural that the docs don't get quite the same level of attention, but hopefully, Rambda continues receiving love and care in the future.