util
module
These are common low-level utilities which can be found in many libraries but were implemented locally instead to minimize the library size. And, since they’ve been implemented already, I might as well expose them for external usage.
identity (x)
Returns its argument.
identity(x) // ⇒ x
type (x)
Returns type of its argument (that is, the type constructor, or x
itself for nil values).
type(new Date) // ⇒ Date
type(/x/) // ⇒ RegExp
type([]) // ⇒ Array
type({}) // ⇒ Object
type(0) // ⇒ Number
type(null) // ⇒ null
type(void 0) // ⇒ undefined
keys (o)
Returns keys of its argument (same as Object.keys(x)
except it doesn’t fail on nil values);
in case of an Array
its keys
are stringified indices.
keys({foo: 1, bar: 2, baz: 3}) // ⇒ ['foo', 'bar', 'baz']
keys(['a', 'b', 'c']) // ⇒ ['0', '1', '2']
keys(null) // ⇒ []
keys(void 0) // ⇒ []
vals (o)
Returns vals of its argument (same as Object.values(x)
except it doesn’t fail on nil values);
in case of an Array
its vals
are simply its items.
vals({foo: 1, bar: 2, baz: 3}) // ⇒ ['foo', 'bar', 'baz']
vals(['a', 'b', 'c']) // ⇒ ['0', '1', '2']
vals(null) // ⇒ []
vals(void 0) // ⇒ []
entries (o)
Returns key-value pairs of its argument in unspecified order (same as Object.entries
except it doesn’t fail on nil values).
entries({foo: 1, bar: 2, baz: 3}) // ⇒ [['foo', 1], ['bar', 2], ['baz', 3]]
entries(['a', 'b', 'c']) // ⇒ [['0', 'a'], ['1', 'b'], ['2', 'c']]
dict (kvs)
Returns a dictionary built from provided key-value pairs (same as Object.fromEntries
except it doesn’t fail on nil values).
dict([['foo', 1], ['bar', 2], ['baz', 3]]) // ⇒ {foo: 1, bar: 2, baz: 3}
isArray (x)
Checks if its argument is an Array
(alias to Array.isArray(x)
).
isArray([]) // ⇒ true
isArray({}) // ⇒ false
isArray(null) // ⇒ false
isDict (x)
Checks if its argument is a dictionary, i.e. a plain object used as a by-key collection (its type()
is Object
).
isDict([]) // ⇒ false
isDict(/x/) // ⇒ false
isDict({}) // ⇒ true
isDict(null) // ⇒ false
isFn (x)
Checks if its argument is a function (its type()
is Function
; note that JS classes are also, in fact, functions).
isFn(() => {}) // ⇒ true
isFn(Date) // ⇒ true
isFn(Object) // ⇒ true
isFn({}) // ⇒ false
merge (...os)
Returns a dictionary composed by merging keys from os
into an empty object (alias to Object.assign({}, ...os)
);
merging is done successively left-to-right (right-fold), so in case of repeating keys, the last instance will be used.
merge({foo: 1}, {bar: 2}, {baz: 3}) // ⇒ {foo: 1, bar: 2, baz: 3}
merge({foo: 1, bar: 2}, null, {foo: 3}) // ⇒ {foo: 3, bar: 2}
merge(['a', 'b', 'c']) // ⇒ {0: 'a', 1: 'b', 2: 'c'}
merge() // ⇒ {}
assoc (o, k, v)
Returns a copy of dictionary o
with the value for key k
set to v
(alias to merge(o, {[k]: v})
).
assoc({foo: 1, bar: 2}, 'baz', 3) // ⇒ {foo: 1, bar: 2, baz: 3}
assoc({foo: 1, bar: 2}, 'foo', 3) // ⇒ {foo: 3, bar: 2}
assoc([1, 2, 3], 1, 4) // ⇒ [1, 4, 3]
assoc([1, 2, 3], 4, 9) // ⇒ [1, 2, 3, , 9]
assoc([1, 2, 3], 'foo', 42) // ⇒ {'0': 1, '1': 2, '2': 3, foo: 42}
assoc([1, 2, 3], -4, 42) // ⇒ {'0': 1, '1': 2, '2': 3, '-4': 42}
assoc(null, 'answer', 42) // ⇒ {answer: 42}
dissoc (o, ...ks)
Returns a copy of dictionary o
without the keys ks
.
dissoc({foo: 1, bar: 2, baz: 3}, 'bar', 'baz') // ⇒ {foo: 1}
dissoc([1, 2, 3], 1, 4) // ⇒ [1, , 3]
dissoc(null, 'foo') // ⇒ {}
update (o, k, f, ...args)
Returns a copy of dictionary o
with the value k
updated by calling the function f
on it
(same as assoc(o, k, f(o[k], ...args))
except it doesn’t fail on a missing key).
update({answer: 42}, 'answer', n => n+1) // ⇒ {answer: 42}
update({foo: {bar: 1}}, 'foo', assoc, 'baz', 2) // ⇒ {foo: {bar: 1, baz: 2}}
update([1, 2, 3], 1, n => n-3) // ⇒ [1, -1, 3]
getIn (o, path)
Returns the value in a nested structure, where path
is a sequence of keys (e.g. getIn(o, [foo, bar, baz])
is equivalent to o[foo][bar][baz]
, except it doesn’t fail on missing keys); getIn
can be used
as a trivial extractor (e.g. rf.regSub('foo', getIn)
implements the ['foo']
query –
as well as ['foo', 'items']
, ['foo', 'items', 4]
, etc.).
getIn({foo: {bar: 1}}, ['foo', 'bar']) // ⇒ 1
getIn([{answer: 42}], [0, 'answer']) // ⇒ 42
getIn({foo: {bar: 1}}, ['baz', 'bar']) // ⇒ undefined
getIn(null, ['foo', 'bar']) // ⇒ undefined
assocIn (o, path, v)
Returns a copy of dictionary o
with the value for getIn(it, path)
set to v
;
if part of the path is missing, an empty dict is provided instead (thus, path
is guaranteed to exist).
assocIn({foo: {bar: 1}}, ['foo', 'bar'], 42) // ⇒ {foo: {bar: 42}}
assocIn([{answer: 12}], [0, 'answer'], 42) // ⇒ [{answer: 42}]
assocIn({foo: {bar: 1}}, ['baz', 'bar'], 42) // ⇒ {foo: {bar: 1}, baz: {bar: 42}}
assocIn(null, ['foo', 'bar'], 42) // ⇒ {foo: {bar: 42}}
updateIn (o, path, f, ...args)
Returns a copy of dictionary o
with the value for getIn(it, path)
set to f(v, ...args)
(where v
is the previous value);
if part of the path is missing, an empty dict is provided instead (thus, path
is guaranteed to exist).
updateIn({foo: {bar: 1}}, ['foo', 'bar'], n => n+1) // ⇒ {foo: {bar: 2}}
updateIn([{answer: 12}], [0, 'answer'], ((a, b) => a-b), 2) // ⇒ [{answer: 10}]
updateIn({foo: {bar: 1}}, ['baz', 'bar'], () => 42) // ⇒ {foo: {bar: 1}, baz: {bar: 42}}
updateIn(null, ['foo', 'bar'], () => 42) // ⇒ {foo: {bar: 42}}
let dissocIn = (o, path, ...ks) => updateIn(o, path, dissoc, ...ks);
chunks (xs, n)
Returns the array passed to it split into chunks the size of n
;
leftover values are put in an extra chunk (equivalent to (partition-all n xs)
).
chunks([1, 2, 3, 4, 5], 2) // ⇒ [[1, 2], [3, 4], [5]]
chunks("abcdef", 3) // ⇒ ["abc", "def"]
chunks("", 2) // ⇒ []
flatten (xs)
Returns a flattened version of the provided array (with every element in nested arrays insterted in place of those arrays, until there’s no array elements left).
flatten([[1], [2, [3], 4], 5]) // ⇒ [1, 2, 3, 4, 5]
repr (x)
Returns a normalized stringified version of its argument; equivalent to JSON.stringify(x)
,
except the dict keys are sorted, and RegExp
values are replaced with their .toString()
(e.g. /x/g
→ "/x/g"
);
used as a workaround for Map
only supporting identity comparison.
repr( [{foo: 1, bar: 2, baz: /3/}] ) // ⇒ `[{"bar": 2, "baz": "/3/", "foo": 1}]`
identical (a, b)
Is a by-pointer identity comparison; it’s the comparison function used by reagent
and re-frame
modules for most ratom update checks.
The difference from ===
is that it also checks for NaN
(so that any value is identical to itself).
identical("foo", "foo") // ⇒ true
identical(NaN, NaN) // ⇒ true
eq (a, b)
Is a by-value deep-comparison of plain data structures (supporting arrays and dictionaries);
it’s the comparison function used by re-frame
module for db update checks (can be overridden by calling _init({eq: …})
).
eq([{foo: 1, bar: NaN}], [{bar: NaN, foo: 1}]) // ⇒ true
eqShallow (a, b)
Is a by-value shallow-comparison of plain data structures (supporting arrays and dictionaries); it’s used for comparing flat data structures.
eqShallow({foo: 1, bar: NaN}, {bar: NaN, foo: 1}) // ⇒ true
eqShallow(['foo', 'bar', NaN], ['foo', 'bar', NaN]) // ⇒ true
chain (x, ...fns)
Threads x
through the provided list of functions, passing either x
or result of previous function as the first argument;
extra arguments can be passed if you wrap the function in an array.
chain({foo: 1, bar: 2, baz: 3},
[merge, {answer: 42}],
[dissoc, 'foo'],
keys)
// ⇒ ['bar', 'baz', 'answer']
multi (dispatch=identity)
Is a simple multimethod generator; it produces a function which dispatches using the dispatch
function
(by identity comparison; default dispatch is by 1st argument) over the map of provided implementations.
.when (x, f)
sets the dispatched valuex
to implementationf
(returns the multimethod so it can be chained).default (f)
sets the fallback dispatch tof
(without it aTypeError
is thrown instead; also returns the multimethod); equivalent to(defmethod … :default …)
let foo = multi((...args) => args.length).when(0, () => "").when(1, x => `[${x}]`) foo() // ⇒ "" foo('bar') // ⇒ "[bar]" foo('bar', 'baz', 42) // TypeError("Invalid arguments") foo.when(2, (k, v) => `{${k}: ${v}}`) foo('bar', 'baz') // ⇒ "{bar: baz}" foo.default((...args) => repr(args)) foo('bar', 'baz', 42) // ⇒ `["bar","baz",42]`