The package containing all 103 individual @unctionjs packages as a single package.
Using
All functions are bound by these principles:
- All functions will have a description and an example.
- All functions will have a test for each type they support.
- All functions will have type annotations.
- All functions will only take a single argument.
- All inner functions will be named based on the outer function and it's argument name, to improve debugging readability.
- Functions that deal with types have a common standard format (see:
type()
) - Functions that mutate the original value, though rare, will have a suffix of M.
- Functions that take or return promises will have a suffix of P.
- Functions that can work on one type of Functor (value that can be mapped) can work on another type, including:
- List (Array, Set)
- Record (Object, Map)
- Text (String, Buffer)
- Stream (Stream, MemoryStream)
You can use each of these packages individually:
import {hammer} from "@unction/complete"
Raison d'exister
There are a few similar libraries out there and they all have good standing with the community, so why make @unctionjs? Well the original package started off as ramda-extra, a set of functions in a package that ramda seemed to never want to implement (like at the time mapKeys). Then eventually I got to the point where I wanted to have functions be curried for clarity and found that many ramda functions don't fully support only currying. While ramda is amazing and I still use it to this day I knew I had to fork off and write my own path.
Here's a list of (I believe) fair reasons why you should use unction over these popular and really good libraries:
- ramda: Ramda has all functions in a single package, it relies on internal private functions to ensure compatibility, does not have real type checking, prefers "autocurrying" which can lead to issues with curried functions, and finally as described above ramda has an interest in retaining a small surface layer
- lodash: Lodash only does curried as a second class citizen, doesn't have type checking, prefers autocurrying when it has support for it, and doesn't have a very clear picture about what some of the functions should work on
RecordType => Promise<RecordType>
This takes an object where the values are probably promises and returns a promise that has that same object but with the resolved values.
Here's a good example of this function in use:
function signUp (attributes, slug) {
return function thunk (dispatch, getState, {client}) {
return allObjectP({
loading: startLoading(slug),
session: pushSession(attributes, client)
})
.then(({session}) => {
return allObjectP({
merge: mergeResource(session),
current: storeCurrent(session.id),
account: pullAccount(session.relationship.account.data.id, client),
})
})
.then(({account}) => {
return {
merge: mergeResource(account),
current: storeCurrent(account.id),
}
})
.then(() => stopLoading(slug))
.then(() => dispatch({type: "signUp"}))
.catch(logger.error.bind(error))
}
}
If we use allP
or Promise.all
we're getting an array back, but that's annoying to destructure. The allObjectP
function gives us the concurrency we want with a named interface for the returned resolutions.
allP()
Array<any | Promise<any>> -> Promise<Array<any>>
A port of the Promise.all()
function.
Credit: @keithamus
always()
any -> any -> any
Always returns the value given when called
always(1)() // 1
always(1)(0) // 1
append()
mixed -> (ArrayType | string) -> (ArrayType | string)
Takes a value and puts it at the end of the given list.
append(4)([5]) // => [5, 4]
append("c")("ab") // => "abc"
NOTE: While there is a type annotation in the README, this function cannot have type annotations due to a bug in flow.
appendM()
any -> Array<any> -> Array<any>
Takes an array and an item and returns the combination of both, appended.
NOTE: This mutates the array
const data = [1, 2, 3]
appendM(4)(data)
Would return:
[1, 2, 3, 4]
(ValueType -> any) -> any
Takes a function and a value and applies that function to that value.
applicator(inc)(1) // 1
(Array<mixed -> mixed> | RecordType<KeyType, mixed -> mixed>)<T> -> (ArrayType | RecordType)<T> -> (ArrayType | RecordType)<T>
Takes a list of functions and a list of values and applies the values to the functions.
applicators([
recordfrom(["point", "x"]),
recordfrom(["point", "y"]),
recordfrom(["point", "z"]),
])([
40.453,
2.2,
423.0,
])
returns
[
{point: {x: 40.453}},
{point: {y: 2.2}},
{point: {z: 423.0}},
]
applicators({
x: inc,
y: dec
})({
x: -1,
y: 1
})
returns
{
x: 0,
y: 0
}
arrayify()
any -> [any] | Array<any>
Takes a value and turns it into an array of that value, unless the value is already an array.
arrayify("a")
returns
["a"]
arrayify(["a"])
returns
["a"]
aside()
Array<mixed => mixed> -> mixed -> mixed
Takes a stack of functions, like pipe()
, but always returns the second argument.
pipe(
aside([(value) => value.toLowerCase(), console.log]),
processData
)(
"Hello, world"
) // "Hello, world"
But also logs:
"hello, world"
attach()
ObjectKeyType => ValueType => ObjectType => ObjectType
MapKeyType => ValueType => MapType => MapType
ArrayKeyType => ValueType => ArrayType => ArrayType
null => ValueType => SetType => SetType
null => ValueType => StreamType => StreamType
A polymorphic way to attach a value to the key on a keyed functor. When dealing with a sorted list type and the key is larger than the list, it will append to the list. When the key is an index that already exists it will place the value at that index and shift remaining values to the right.
attach("hello")("world")({}) // => {hello: "world"}
attach(3)("x")([1, 2, 3]) // => [1, 2, 3, "x"]
attach(1)("x")([1, 2, 3]) // => [1, "x", 2, 3]
attach(null)("x")(new Set([1, 2, 3])) // => {1 2 3 "x"}
attach(10)("x")([]) // => ["x"]
attach(0)("a")("bc") // => "abc"
attach(null)("a")(xstream.of("b")) // => a---b--->
Array<UnfinishedKeyChainType> -> FunctorType -> ValueType
Cascades through multiple keychains in order to arrive at a value. Null keys are replaced with the previous keychain's value.
cascadingKeyChain(
[
["ephemeral", "current", "session"],
["resources", "sessions", null, "relationships", "account", "data", "id"],
["resources", "accounts", null, "attributes", "name"]
]
)(
{
ephemeral: {current: {session: "1"}},
resources: {
sessions: {
1: {
id: "1",
relationships: {account: {data: {id: "2"}}},
},
},
accounts: {
2: {
id: "2",
attributes: {name: "Kurtis Rainbolt-Greene"},
},
},
},
}
)
returns
"Kurtis Rainbolt-Greene"
catchP()
(any -> any) -> Promise<any> -> Promise<any>
A port of the Promise.prototype.catch()
function.
Credit: @keithamus
compact()
Array<any> -> Array<mixed>
Takes a collection (Array or Object) and returns a copy of that value without null
or undefined
values.
avatarUrls // => [null, "/1.jpg", null, "/3.jpg"]
compact(avatarUrls) // => ["/1.jpg", "/3.jpg"]
head(users) // {"avatar": null, "name": "Kurtis Rainbolt-Greene"}
compact(head(users)) // {"name": "Kurtis Rainbolt-Greene"}
(FunctorType -> mixed) -> KeyChainType -> FunctorType -> FunctorType
Given an object this function will return that object but with a new property, where the value is computed. The computation is given the object you'll be copying.
const computer = ({id, attributes: {username}}) => `${username}#${id}`
const key = "tag"
const payload = {
id: "1",
attributes: {
username: "krainboltgreene"
}
}
computedProp(computer)(key)(payload)
Would return:
{
id: "1",
tag: "krainboltgreene#1",
attributes: {
username: "krainboltgreene"
}
}
const multiKey = ["attributes", "tag"]
computedProp(computer)(key)(payload)
Would return:
{
id: "1",
attributes: {
tag: "krainboltgreene#1",
username: "krainboltgreene"
}
}
couple()
mixed -> mixed -> [mixed, mixed]
Takes any value and then any value and returns an array containing those values.
couple(4)(5) // => [4, 5]
DOMEventsConfigurationType -> DOMEventNameType -> DOMStreamType -> DOMEventStreamType
Takes a configuration, an event name, and a DOM source and returns an observable of that event type
domEvents({})("click")(DOM)
returns
--click--click--click-->
domEventsManyConfigurationType => Array<DOMEventNameType> => DOMEventStreamType
Takes many event names and returns an observable of those events.
domEventsMany({})(["click", "input"])(DOM)
returns
--click--input--input--click--input
endsWith()
string -> string -> boolean
Determines if a given subset of text is at the end of another set of text.
endsWith("!")("Hello, world!") // true
everyP()
(Array<any | Promise<any>>) -> Promise<[ResolvedPromisesType, RejectedPromisesType]>
Returns both resolved and rejected promises as distinct lists.
((any -> any) -> FunctorType -> FunctorType) -> (any -> any) -> number -> FunctorType -> FunctorType
Takes a tree and creates a single object where the root keys are conjoined nested keys.
flattenTree({
data: {
profile: {
name: "Kurtis Rainbolt-Greene"
age: 24
},
metadata: {
interval: "10s"
},
location: "http://api.example.com/profiles/24"
}
})
Would return:
{
"data-profile-name": "Kurtis Rainbolt-Greene",
"data-profile-age": 24,
"data-interval": "10s",
"data-location": "Kurtis Rainbolt-Greene"
}
flip()
(any -> any) -> any -> any -> any
Flips a function's first and second arguments.
flip(key)({aaa: "1"})("aaa") // "1"
forEach()
(any -> KeyType -> any) -> FunctorType -> FunctorType
Takes any kind of iterable object and figures out the best way to iterate over it.
forEach((x) => y)([])
forEach((x) => y)(new Map)
forEach((x) => y)({})
fresh()
mixed -> mixed
Takes a value and returns an empty fresh version of that value.
fresh({aaa: "aaa"}) // {}
fresh(["aaa"]) // []
fresh({}) // {}
fresh([]) // []
hammer()
KeyType -> RecordType -> RecordType
Use this to de-nest a nested object.
const payload = {
id: 1
attributes: {
name: "Kurtis Rainbolt-Greene",
age: 26
}
}
hammer("attributes")(payload)
Which returns:
{
id: 1,
name: "Kurtis Rainbolt-Greene",
age: 26
}
PredicateFunctionType => UnaryFunctionType => UnaryFunctionType => mixed
Based on a predicate it passes the value to a consequent or alternative function
ifThenElse(isEven)(toString)(toFloat)(1) // 1.0
ifThenElse(isEven)(toString)(toFloat)(2) // "2"
isArray()
mixed -> boolean
Takes any value and then any value and returns an array containing those values.
isArray([]) // => true
isArray({}) // => false
isArray("") // => false
mixed -> boolean
Takes any value and then any value and returns an array containing those values.
isIterable({}) // => true
isIterable([]) // => true
isIterable("") // => true
isIterable(1) // => false
isNil()
mixed -> boolean
Determines if a value is not a value.
isNil(null) // true
isNil(undefined) // true
isNil(0) // false
isNil("") // false
isNil([]) // false
isNil({}) // false
isObject()
mixed -> boolean
Takes a value and determines if it's an object.
isObject({}) // => true
isObject([]) // => false
isObject("") // => false
FunctorType -> boolean
Allows you to check if a iterable has any items.
isPopulated([1]) // true
isPopulated({a: 'b'}) // true
isPopulated({}) // false
isPopulated([]) // false
isPopulated("") // false
isPopulated("a") // true
mixed -> boolean
This lets you know if it's a non-null, non-undefined value.
isPresent('x') // true
isPresent([]) // true
isPresent(null) // false
isPresent(undefined) // false
isType()
string -> mixed -> boolean
Takes any value and then any value and returns an array containing those values.
isType("Object")({}) // => true
isType("Array")([]) // => true
isType("String")("") // => true
itself()
any -> any
Always returns the value given when calling.
itself(1) // 1
itself(1) // 1
key()
KeyType -> mixed -> ValueType
Returns the value of a specific key on an iterable. If no key is found it returns undefined. If the second argument isn't an iterable we return undefined, to allow for graceful failure.
key("aaa")({aaa: "1"}) // "1"
key("bbb")({aaa: "1"}) // undefined
key("bbb")(undefined) // undefined
key(0)(["aaa"]) // "aaa"
keyChain()
KeyChainType -> TreeType -> ValueType
Takes a chain of keys and a tree, traversing down and reaching the last value. If any part of the chain is undefined or not an object the result will always be undefined.
keyChain(["aaa", "bbb", "ccc"])({aaa: {bbb: {ccc: "1"}}}) // "1"
keyChain(["aaa", "ddd", "ccc"])({aaa: {bbb: {ccc: "1"}}}) // undefined
StringType => StringType => boolean
Determines if a set of text does not have a subset of text.
const data = "I love pies!"
const lacksBestFood = lacksText("pizza")
lacksBestFood(data) // false
mapKeys()
(KeyType -> KeyType) -> FunctorType -> FunctorType
Like ramda's map, but instead of the value it maps over keys.
const attributes = {
name: "Kurtis Rainbolt-Greene",
createdAt: new Date()
}
mapKeys(kebab)(attributes)
Would return:
{
name: "Kurtis Rainbolt-Greene",
"created-at": new Date()
}
(ValueType => KeyType => KeyType) -> FunctorType -> FunctorType
Map over keys with the context of the value and key.
const attributes = {
name: "Kurtis Rainbolt-Greene",
createdAt: new Date()
}
mapKeys((value) => (key) => )(attributes)
Would return:
{
name: "Kurtis Rainbolt-Greene",
"created-at": new Date()
}
(any -> any) -> FunctorType -> FunctorType
A pretty standard mapValues()
, but with enforced unary currying.
mapValues(
(value) => value + 1
)(
[1, 2, 3]
)
Which will return:
[2, 3, 4]
(ValueType => ObjectKeyType => ValueType) => ObjectType => ObjectType
(ValueType => ArrayKeyType => ValueType) => ArrayType => ArrayType
(ValueType => MapKeyType => ValueType) => MapType => MapType
(ValueType => ArrayKeyType => ValueType) => StringType => StringType
(ValueType => null => ValueType) => SetType => SetType
Just like map, but gives back the index argument (as an integer, not a string if array)
Array<FunctorType> -> FunctorType
Merges a list of iterables (of the same type) into a single iterable.
mergeAllLeft([["0"], ["1"], ["2"]]) // ["2", "1", "0"]
mergeAllLeft([{aaa: "aaa"}, {bbb: "bbb"}, {ccc: "ccc"}]) // {aaa: "aaa", bbb: "bbb", ccc: "ccc",}
Array<FunctorType> -> FunctorType
Merges a list of iterables (of the same type) into a single iterable.
mergeAllRight([["0"], ["1"], ["2"]]) // ["0", "1", "2"]
mergeAllRight([{aaa: "aaa"}, {bbb: "bbb"}, {ccc: "ccc"}]) // {aaa: "aaa", bbb: "bbb", ccc: "ccc",}
FunctorType => FunctorType => FunctorType
Recursively merges two objects/arrays. Merges objects with merge
and arras with concat. Prefers left. THAT IS ALL.
const left = {
alpha: "1"
}
const right = {
beta: "2"
}
mergeDeepLeft(left)(right)
{
alpha: "1",
beta: "2"
}
const left = {
alpha: {
alpha1: "1"
}
}
const right = {
beta: {
beta1: "1"
}
}
mergeDeepLeft(left)(right)
{
alpha: {
alpha1: "1"
},
beta: {
beta1: "1"
}
}
const left = {
alpha: [
"1"
]
}
const right = {
alpha: [
"1"
]
}
mergeDeepLeft(left)(right)
{
alpha: [
"1",
"1"
]
}
FunctorType -> FunctorType -> FunctorType
Recursively merges two objects/arrays. Merges objects with merge
and arras with concat. Prefers right. THAT IS ALL.
const left = {
alpha: "1"
}
const right = {
beta: "2"
}
mergeDeepRight(left)(right)
{
alpha: "1",
beta: "2"
}
const left = {
alpha: {
alpha1: "1"
}
}
const right = {
beta: {
beta1: "1"
}
}
mergeDeepRight(left)(right)
{
alpha: {
alpha1: "1"
},
beta: {
beta1: "1"
}
}
const left = {
alpha: [
"1"
]
}
const right = {
alpha: [
"1"
]
}
mergeDeepRight(left)(right)
{
alpha: [
"1",
"1"
]
}
FunctorType -> FunctorType -> FunctorType
Merges two iterables, preferring left.
const left = {
alpha: "1",
beta: "1"
}
const right = {
beta: "2",
zeta: "3"
}
mergeLeft(left)(right)
Which returns:
{
alpha: "1",
beta: "1",
zeta: "3"
}
FunctorType -> FunctorType -> FunctorType
Merges two iterables, preferring right.
const left = {
alpha: "1",
beta: "1"
}
const right = {
beta: "2",
zeta: "3"
}
mergeRight(left)(right)
Which returns:
{
alpha: "1"
beta: "2",
zeta: "3"
}
(ValueType -> ValueType -> ValueType) -> FunctorType -> FunctorType -> FunctorType
Merges two iterables and uses a provided function to handle conflicts. The function is given the the left value and the right value.
const left = {
alpha: "0",
beta: "1",
zeta: "3"
}
const right = {
alpha: "0",
beta: "2",
zeta: "3"
}
mergeWith((l) => (r) => l+r)(left)(right)
Which returns:
{
alpha: "0",
beta: "12",
zeta: "3"
}
(FunctorType -> FunctorType -> KeyType -> any) -> FunctorType -> FunctorType -> FunctorType
Merges two iterables and uses a provided function to handle conflicts. The function is given the key, the left value, and the right value.
const left = {
beta: "1"
}
const right = {
beta: "2"
}
mergeWith((key, leftValue, rightValue) => key+leftValue+rightValue)(left)(right)
Which returns:
{
beta: "beta12"
}
((any -> any) -> FunctorType -> FunctorType) -> (any -> any) -> number -> FunctorType -> FunctorType
Takes a function (the application) that takes function(s) (later referred to as
the inner) and value(s) (map()
, forEach()
, find()
), a function (the inner)
that will be applied to a value(s), and finally a number (depth) to apply that
applicator around the inner.
In the below example we want to take two sets of records and index them by id:
const records = [
[
{
id: "a1",
type: "commercial",
},
{
id: "a2",
type: "commercial",
}
],
[
{
id: "b1",
type: "residential",
},
{
id: "b2",
type: "residential",
}
]
]
Normally we'd just do mapValues(indexBy(key("id")))
, however we can make this easier and dynamic:
const nestedIndexById = nestedApply(mapValues)(indexBy(key("id")))(1)
nestedIndexById(records)
And the result:
[
{
a1: {
id: "a1",
type: "commercial",
},
a2: {
id: "a2",
type: "commercial",
},
},
{
b1: {
id: "b1",
type: "residential",
},
b2: {
id: "b2",
type: "residential",
},
},
]
Array<mixed | Promise<mixed>> -> Promise<Array<mixed>>
Will take an array of promises and returns a promise of only the resolved promises.
Array<[string, any]> -> Array<string>
Takes an array that looks like a list of pairs (key, values) and returns all the keys.
Lets say you have this data:
const data = {
a: 1,
b: 2,
c: 3,
}
And you turn it into pairs:
const pairings = toPairs(data)
You would end up with this:
[
['a', 1],
['b', 2],
['c', 3],
]
Now you just want the keys:
pairsKeys(pairings)
You would get the following:
[
'a',
'b',
'c',
]
Array<[KeyType, any]> -> Array<any>
Takes an array that looks like a list of pairs (key, values) and returns all the values.
Lets say you have this data:
const data = {
a: 1,
b: 2,
c: 3,
}
And you turn it into pairs:
const pairings = toPairs(data)
You would end up with this:
[
['a', 1],
['b', 2],
['c', 3],
]
Now you just want the keys:
pairsValues(pairings)
You would get the following:
[
1,
2,
3,
]
PredicateFunctionType => FunctorType => [FunctorType, FunctorType]
This function takes an functgor and returns an array of two of the same type of functor. the first of which contains elements which satisfy the predicate, the second of which contains element which do not.
partition(isOdd)([1,2,3,4]) // [[1,3],[2,4]]
pluck()
KeyChainType -> FunctorType -> Array<any>
Given a keychain and records return the values at the keychain for each record.
pluck(
["attributes", "name"]
)(
[
{
id: "1",
attributes: {
name: "Kurtis",
age: 29,
height: "5'10\"",
},
},
{
id: "2",
attributes: {
name: "Chris",
age: 29,
height: "5'8\"",
},
},
]
)
Which will return:
[
"Kurtis",
"Chris"
]
plucks()
Array<KeyChainType> -> FunctorType -> Array<any>
Given keychain and records, return the values at the keychain for each record.
plucks(
[
["attributes", "name"],
["attributes", "age"],
["attributes", "friends"],
["id"]
]
)(
[
{
id: "1",
attributes: {
name: "Kurtis",
age: 29,
height: "5'10\"",
},
},
{
id: "2",
attributes: {
name: "Chris",
age: 29,
height: "5'8\"",
},
},
]
)
Which will return:
[
["Kurtis", 29, null, "1"],
["Chris", 29, null, "2"]
]
prepend()
mixed -> (ArrayType | string) -> (ArrayType | string)
Takes a value and puts it at the beginning of the given list.
prepend(4)([5]) // => [4, 5]
prepend("c")("ab") // => "cab"
NOTE: While there is a type annotation in the README, this function cannot have type annotations due to a bug in flow.
KeyChain -> any -> FunctorType
Given a keychain and a value it creates an object that has keys based on the keychain.
recordFrom(["key", "subkey"])("value")
Which returns:
{
key: {
subkey: "value"
}
}
(AccumulatedType -> ValueType -> AccumulatedType) -> InitialType -> FunctorType -> AccumulatedType
Reduce over a iterable's values.
reduceValues(
(accumulation) => (current) => `${accumulation}/${current}`
)(
"~"
)(
["Users", "krainboltgreene", "Code"]
)
Which will return:
"~/Users/krainboltgreene/Code"
(mixed => ValueType => (KeyType | void) => mixed) => mixed => FunctorType => mixed
Reduces over a functor, providing the reducer with the value and key.
reduceWithValueKey(
(accumulation) => (current) => (key) => `${accumulation}/${current}:${key}`
)(
"~"
)(
["Users", "krainboltgreene", "Code"]
)
Which will return:
"~/Users:0/krainboltgreene:1/Code:2"
rejectP()
any -> Promise<any>
A port of the Promise.reject()
function.
Credit: @keithamus
PredicateFunctionType => mixed => FunctorType
Replaces values in an functor with another value based on a predicate.
replaceWhen(isEven)(null)([1, 2, 3]) // [1, null, 3]
resolveP()
any -> Promise<any>
A port of the Promise.resolve()
function.
Credit: @keithamus
sample()
(string | Array<any>) -> (string | any)
Takes an Array or string and randomly one element to return.
users() // => [{"id": 1, "name": "Kurtis Rainbolt-Greene"}, {"id": 2, "name": "Angela Englund"}]
sample(users()) // => {"id": 2, "name": "Angela Englund"}
sample(users()) // => {"id": 2, "name": "Angela Englund"}
sample(users()) // => {"id": 1, "name": "Kurtis Rainbolt-Greene"}
number -> (string | Array<any>) -> (string | any)
Takes an Array or string and randomly picks n elements to return, but never the same one.
users() // => [{"id": 1, "name": "Kurtis Rainbolt-Greene"}, {"id": 2, "name": "Angela Englund"}]
sample(1)(users()) // => [{"id": 2, "name": "Angela Englund"}]
sample(2)(users()) // => [{"id": 2, "name": "Angela Englund"}, {"id": 1, "name": "Kurtis Rainbolt-Greene"}]
shuffle()
OrderedFunctorType => OrderedFunctorType
Takes an Ordered Functor and returns an Ordered Functor with the same content, but in a random order.
users()
Would return:
[{"id": 1, "name": "Kurtis Rainbolt-Greene"}, {"id": 2, "name": "Angela Englund"}, {"id": 3, "name": "Joshua Benitez"}]
shuffle(users())
Would return:
[{"id": 1, "name": "Kurtis Rainbolt-Greene"}, {"id": 3, "name": "Joshua Benitez"}, {"id": 2, "name": "Angela Englund"}]
shuffle(users())
Would return:
[{"id": 3, "name": "Joshua Benitez"}, {"id": 1, "name": "Kurtis Rainbolt-Greene"}, {"id": 2, "name": "Angela Englund"}]
shuffle(users())
Would return:
[{"id": 2, "name": "Angela Englund"}, {"id": 3, "name": "Joshua Benitez"}, {"id": 1, "name": "Kurtis Rainbolt-Greene"}]
splat()
(ValueType -> any) -> Array<ValueType> -> any
Takes a function and a list of values and recursively applies the value to the functions.
splat((a) => (b) => a + b)([1, 2]) // 3
string -> string -> boolean
Determines if a given subset of text is at the start of another set of text.
startsWith("Hello")("Hello, world!") // true
(any -> any) -> (any -> any) -> Promise<any> -> Promise<any>
A port of the Promise.prototype.then()
function, but with the extra catch argument.
Credit: @keithamus
thenP()
(any -> any) -> Promise<any> -> Promise<any>
A port of the Promise.prototype.then()
function.
Credit: @keithamus
thrush()
any -> (any -> any) -> any
One of the fantasy birds: it takes a value, a function, and then applies that value to as the first argument to that function.
thrush(0)((value) => `${value}`) // "0"
treeify()
Array<(any -> FunctorType -> FunctorType)> -> Array<FunctorType> -> FunctorType
This takes a list of functions (the folders) and an array of objects or an object of objects (the collection) to create a tree. Each function in the list of folders will in some way return a new object. All of the objects produced are then turned into a final tree.
const collection = [
{
id: "a1",
type: "resources",
attributes: {
version: "v1",
namespace: "accounts",
}
},
{
id: "a2",
type: "resources",
attributes: {
version: "v1",
namespace: "accounts",
}
},
{
id: "b1",
type: "resources",
attributes: {
version: "v1",
namespace: "profiles",
}
},
{
id: "b1",
type: "resources",
attributes: {
version: "v2",
namespace: "profiles",
}
}
]
The order goes from outer layer to deeper layer, so in this case the outer
level properties will be based on key("type")
, and the deepest layer
properties will be based on key("id")
.
const functions = [
groupBy(key("type")),
groupBy(keyChain(["attributes", "namespace"])),
groupBy(keyChain(["attributes", "version"])),
indexBy(key("id")),
]
treeify(functions)(collection)
The resulting object looks like this:
{
resources: {
accounts: {
v1: {
a1: {
id: "a1",
type: "resources",
attributes: {
version: "v1",
namespace: "accounts",
}
},
a2: {
id: "a2",
type: "resources",
attributes: {
version: "v1",
namespace: "accounts",
}
}
}
},
profiles: {
v1: {
b1: {
id: "b1",
type: "resources",
attributes: {
version: "v1",
namespace: "profiles",
}
}
},
v2: {
b1: {
id: "b1",
type: "resources",
attributes: {
version: "v2",
namespace: "profiles",
}
}
}
}
}
}
type()
null | void | {constructor: {name: string}} -> string
Returns the type name of the value provided.
type("a") // "String"
type(1) // "Number"
type({}) // "Object"
type([]) // "Array"
type(true) // "Boolean"
type(null) // "null"
type(undefined) // "undefined"
upTo()
number -> Array<number>
Just takes a maximum and produces an array of 1 to that number.
KeyType -> FunctorType -> FunctorType
Returns a copy of an iterable without a key, no matter how deep the tree.
withoutKeyRecursive("__abstraction__")(
{
id: "1",
name: "Kurtis Rainbolt-Greene",
attributes: {
version: "v1",
namespace: "accounts",
__abstraction__: {errors: []},
},
__abstraction__: {errors: []},
}
)
Which will return:
{
id: "1",
name: "Kurtis Rainbolt-Greene",
attributes: {
version: "v1",
namespace: "accounts",
},
}
zip()
(ArrayType | ObjectType) -> ArrayType | ObjectType -> ArrayType | ObjectType
Takes two iterables and merges them together, combining their values into an array
zip([1, 2, 3])([4, 5, 6])
returns
[[1, 4], [2, 5], [3, 6]]
zip({x: 1, y: 2, z: 0})({x: 0, y: 0, z: 0})
returns
{x: [1, 0], y: [2, 0], z: [0, 0]}
values()
FunctorType -> Array<ValueType>
Takes an iterable and returns it's values.
values({aaa: "111", bbb: "222"}) // ["111", "222"]
values(["aaa", "bbb"]) // ["aaa", "bbb"]
(AccumulatedType -> KeyType -> AccumulatedType) -> InitialType -> FunctorType -> AccumulatedType
Reduce over a iterable's keys.
reduceValues(
(accumulation) => (current) => `${accumulation}/${current}`
)(
"~"
)(
["Users", "krainboltgreene", "Code"]
)
Which will return:
"~/0/1/2"
keys()
KeyedFunctorType -> Array<KeyType>
Takes a keyed iterable and returns the keys as an Array.
keys({aaa: "111", bbb: "222"}) // ["aaa", "bbb"]
keys(["111", "222"]) // [0, 1]
pipe()
Array<mixed -> mixed> -> mixed -> mixed
Takes a list of functions and runs a value through that stack from left to right.
pipe([toString, toInteger])(0) // 0
Array<[KeyType, ValueType]> => ObjectType
Takes an array that looks like a primitive Object and turns it into a proper object. Duplicate keys get overwritten.
fromArrayToObject([["aaa", "1"], ["bbb", "2"]]) // {aaa: 1, bbb: 2}
split()
StringType => (StringType | Regexp) => Array<StringType>
Splits up a string by a delimiter.
split(" ")("a b") // ["a", "b"]
split(/-+/)("a---b") // ["a", "b"]
string => (mixed => mixed => boolean) => (string => position => any | any => any) => StreamType => any
Takes a marble string, an assertion, a final state callback, and a stream so that you can assert in tests how a stream will function. Each marble should be deliniated by a "---"
notation. If the last marble node is a "|" then it will make sure the stream has ended. Each "marble" will be evaluated before being compared.
test(({equal, end}) => {
const left = xstream.of("a")
const right = xstream.of("b")
streamSatisfies(
"'b'---'a'---|"
)(
(given) => (expected) => equal(given, expected)
)(
() => () => end()
)(
mergeRight(left)(right)
)
})
test(({equal, end}) => {
const left = xstream.of(1)
const right = xstream.of(2)
streamSatisfies(
"2---1---|"
)(
(given) => (expected) => equal(given, expected)
)(
() => () => end()
)(
mergeRight(left)(right)
)
})
test(({equal, end}) => {
const left = xstream.of({aaa: "aaa"})
const right = xstream.of({bbb: "bbb"})
streamSatisfies(
"{bbb: 'bbb'}--{aaa: 'aaa'}--|"
)(
(given) => (expected) => equal(given, expected)
)(
() => () => end()
)(
mergeRight(left)(right)
)
})
dropLast()
number => (ArrayType | string) => (ArrayType | string)
Returns all but the last N of a list of ordered values.
dropLast(2)([1, 2, 3]) // [1]
dropLast(1)([1, 2, 3]) // [1, 2]
dropLast(2)("abc") // "a"
dropLast(1)("abc") // "ab"
takeLast()
number => (ArrayType | string) => (ArrayType | string)
Returns the last N of a list of ordered values.
takeLast(2)([1, 2, 3]) // [2, 3]
takeLast(1)([1, 2, 3]) // [3]
takeLast(2)("abc") // "bc"
takeLast(1)("abc") // "c"
number => (ArrayType | string) => (ArrayType | string)
Returns the first N of a list of ordered values.
takeFirst(2)([1, 2, 3]) // [1, 2]
takeFirst(1)([1, 2, 3]) // [1]
takeFirst(2)("abc") // "ab"
takeFirst(1)("abc") // "a"
number => (ArrayType | string) => (ArrayType | string)
Returns all but the first N of a list of ordered values.
dropFirst(2)([1, 2, 3]) // [3]
dropFirst(1)([1, 2, 3]) // [2, 3]
dropFirst(2)("abc") // "c"
dropFirst(1)("abc") // "bc"
equals()
mixed => mixed => boolean
Compares two values and attempts to discern if they are truly equal.
equals(true)(true) // true
equals([1, 2, 3])([1, 2, 3]) // true
equals({aaa: "aaa", bbb: "bbb"})({aaa: "aaa", bbb: "bbb"}) // true
equals("abc")("abc") // true
equals(null)(null) // true
equals(undefined)(undefined) // true
equals(false)(true) // false
equals([1, 2, 3])([3, 2, 1]) // false
equals([1, 2, 3])([1]) // false
equals([1, 2, 3])([]) // false
equals({aaa: "aaa", bbb: "bbb"})({aaa: "aaa"}) // false
equals({aaa: "aaa", bbb: "bbb"})({}) // false
equals({aaa: "aaa", bbb: "bbb"})({aaa: "bbb", bbb: "ccc"}) // false
equals("abc")("bac") // false
equals(null)(undefined) // false
equals(undefined)(null) // false
length()
FunctorType -> number
Returns the number of values contained in the iterable.
length([1, 2, 3]) // 3
length({aaa: "aaa", bbb: "bbb"}) // 2
length(new Map([["aaa", "aaa"], ["bbb", "bbb"]])) // 2
length(new Set([1, 2, 3])) // 3
FunctorType => Array<[KeyType?, ValueType?]>
Takes a functor and tries to transform it into a list of key-value pairs.
fromFunctorToPairs({aaa: "a", bbb: "b", ccc: "c"})) // [["aaa", "a"], ["bbb", "b"], ["ccc", "c"]]
fromFunctorToPairs(["a", "b", "c"]) // [[0, "a"], [1, "b"], [2, "c"]]
fromFunctorToPairs(new Map([["aaa", "a"], ["bbb", "b"], ["ccc", "c"]])) // [["aaa", "a"], ["bbb", "b"], ["ccc", "c"]]
fromFunctorToPairs(new Set(["a", "b", "c"])) // [[undefined, "a"], [undefined, "b"], [undefined, "c"]]
IteratorType -> Array<mixed>
Takes an Iterator (SetIterator, MapIterator, etc) and turns it into an array.
fromIteratorToArray(new Set([1, 2, 3]).entries()) // [[1, 1], [2, 2], [3, 3]]
fromIteratorToArray(new Map([["aaa", "a"], ["bbb", "b"], ["ccc", "c"]]).entries()) // [["aaa", "a"], ["bbb", "b"], ["ccc", "c"]]
last()
(ArrayType | string) -> mixed
Returns the last item of an ordered list.
last([1, 2, 3]) // 3
last("abc") // "c"
(ArrayType | string) => (ArrayType | string)
Returns all but the first item in an ordered list
remaining([1, 2, 3]) // [2, 3]
remaining("abc") // "bc"
first()
(ArrayType | string) -> mixed
Returns the first item of an ordered list.
first([1, 2, 3]) // 1
first("abc") // "a"
reverse()
(ArrayType | string) -> (ArrayType | string)
Takes an ordered list type and returns the reverse version of it.
reverse([1, 2, 3]) // [3, 2, 1]
reverse("abc") // "cba"
number => number => boolean
Determines if one number is greater than another number.
greaterThan(1)(0) // true
greaterThan(0)(1) // false
lessThan()
number => number => boolean
Determines if one number is greater than another number.
lessThan(0)(1) // true
lessThan(1)(0) // false
initial()
(ArrayType | string) => (ArrayType | string)
Returns all but the last item in an ordered list.
initial([1, 2, 3]) // [1, 2]
initial("abc") // "ab"
KeyType => KeyedFunctorType => KeyedFunctorType
Takes a key and a keyed functor, returning the keyed functor without the key given.
exceptKey(1)([1, 2, 3]) // [1, 3]
exceptKey(1)("abc") // "ac"
exceptKey("aaa")({aaa: "aaa", bbb: "bbb", ccc: "ccc"}) // {bbb: "bbb", ccc: "ccc"}
exceptKey("aaa")(new Map([["aaa", "aaa"], ["bbb", "bbb"], ["ccc", "ccc"]])) // new Map([["bbb", "bbb"], ["ccc", "ccc"]])
sequence()
ArrayType<MapFunctionType> => mixed => ArrayType
SetType<MapFunctionType> => mixed => SetType
ObjectType<ObjectKeyType, MapFunctionType> => mixed => ObjectType
MapType<MapKeyType, MapFunctionType> => mixed => MapType
Takes a list of functions, a value, and applies that value to each function, returning an array of results.
sequence([increment, decrement])(1) // [2, 0]
sequence(new Set([increment, decrement]))(1) // {2 0}
sequence(new Map([["a", increment], ["b", decrement]]))(1) // {{"a" 2}, {"b" 0}}
sequence({x: increment, y: decrement, z: itself})(1) // {x: 2, y: 0, z: 1}
PredicateFunctionType => FunctorType => FunctorType
Given a predicate and a functor it returns a functor of the same type containing values that returned true for the predicate.
selectValues(isOdd)([1, 2, 3, 4]) // [1, 3]
StringType -> RecordType -> NestedRecordType
Takes a flat record with a specific key pattern and turns it into a nested record.
inflateTree(
"-"
)(
{
"data-profile-name": "Kurtis Rainbolt-Greene",
"data-profile-age": 24,
"data-metadata-interval": "10s",
"data-location": "http://api.example.com/profiles/24",
}
)
which returns
{
data: {
profile: {
name: "Kurtis Rainbolt-Greene",
age: 24,
},
metadata: {interval: "10s"},
location: "http://api.example.com/profiles/24",
},
}
KeyChainType -> KeyChainType -> KeyedFunctorType -> RecordType
Takes a keyed functor that looks like an entity (keys are pointers to both keys and values) and turns them into an non-entity record.
recordFromEntity(
["name"]
)(
["value"]
)(
{
name: "id",
value: "application"
}
)
which returns
{
id: "application"
}
mixed -> StringType
Return the super type of an value.
class A {}
class B extends A {}
supertype(new B()) // "A"