cache(fn)
Returns a function that caches the output values of fn(input)
against input values in a map, such that for each input value
fn
is only ever called once.
A library of functions, observers and streams, published as ES6 modules.
Import functions from the Fn module:
import { get, matches } from '/fn/module.js';
cache(fn)
Returns a function that caches the output values of fn(input)
against input values in a map, such that for each input value
fn
is only ever called once.
compose(fn2, fn1)
Calls fn1
, passes the result to fn2
, and returns that result.
curry(fn [, muteable, arity])
Returns a function that wraps fn
and makes it partially applicable.
choose(fn, map)
Returns a function that takes its first argument as a key and uses it
to select a function in map
which is invoked with the remaining arguments.
Where map
has a function default
, that function is run when a key
is not found, otherwise unfound keys will error.
var fn = choose({
'fish': function fn1(a, b) {...},
'chips': function fn2(a, b) {...}
});
fn('fish', a, b); // Calls fn1(a, b)
once(fn)
Returns a function that calls fn
the first time it is invoked,
and then becomes a noop.
overload(fn, map)
Returns a function that calls a function at the property of object
that
matches the result of calling fn
with all arguments.
var fn = overload(toType, {
string: function a(name, n) {...},
number: function b(n, m) {...}
});
fn('pie', 4); // Returns a('pie', 4)
fn(1, 2); // Returns b(1, 2)
pipe(fn1, fn2, ...)
Returns a function that calls fn1
, fn2
, etc., passing the result of
calling one function to the next and returning the the last result.
weakCache(fn)
Returns a function that caches the return values of fn()
against input values in a WeakMap, such that for each input value
fn
is only ever called once.
The functions below are curried (where they take more than one parameter). They may be partially applied.
argument(n)
Returns a function that returns its nth argument when called.
call(fn)
Returns a function that calls fn()
with no arguments.
equals(a, b)
Perform a deep equality comparison of a
and b
. Returns true
if
they are equal.
get(name, object)
Get property name
of object
.
getPath(path, object)
Returns the value at path
in object
.
const value = getPath('path.to.value', object);
has(key, value, object)
Returns true
if object[key]
is strictly equal to value
.
id(value)
Returns value
.
invoke(name, parameters, object)
Invokes object.name()
with parameters
as arguments. For example:
models.forEach(invoke('save', [version]));
is(a, b)
Perform a strict equality check of a === b
.
isDefined(value)
Check for value – where value
is undefined
, NaN
or null
, returns
false
, otherwise true
.
matches(selector, object)
Where selector
is an object containing properties to be compared against
properties of object
. If they are all strictly equal, returns true
,
otherwise false
.
const vegeFoods = menu.filter(matches({ vegetarian: true }));
noop()
Returns undefined.
not(value)
Returns !value
.
requestTick(fn)
Call fn
on the next tick.
self()
Returns this
.
set(key, object, value)
// Set `input.value` whenever a value is pushed into a stream:
stream.scan(set('value'), input);
setPath(path, object, value)
Sets value
at path
in object
.
stream.each(setPath('prop.value'), object);
toArray(object)
toClass(object)
toFixed(number)
toString(object)
Returns object.toString()
.
toType(object)
Returns typeof object
.
append(str2, str1)
Returns str1 + str2
.
prepend(string1, string2)
Returns str1 + str2
.
prepad(chars, n, string)
Pads string
to n
characters by prepending chars
.
postpad(chars, n, string)
Pads string
to n
characters by appending chars
.
capture(regex, reducers, accumulator, string)
Parse string
with regex
, calling functions in reducers
to modify
and return accumulator
.
Reducers is an object of functions keyed by the index of their capturing
group in the regexp result (0
corresponding to the entire regex match,
the first capturing group being at index 1
). Reducer functions are
called in capture order for all capturing groups that captured something.
Reducers may also define the function ‘close’, which is called at the end
of every capture. All reducer functions are passed the paremeters
(accumulator, tokens)
, where tokens
is the regexp result, and are expected
to return a value that is passed as an accumulator to the next reducer function.
Reducers may also define a function 'catch'
, which is called when a match
has not been made (where 'catch'
is not defined an error is thrown).
const parseValue = capture(/^\s*(-?\d*\.?\d+)(\w+)?\s*$/, {
// Create a new accumulator object each call
0: () => ({}),
1: (acc, tokens) => {
acc.number = parseFloat(tokens[1]);
return acc;
},
2: (acc, tokens) => {
acc.unit = tokens[2];
return acc;
}
}, null);
const value = parseValue('36rem'); // { number: 36, unit: 'rem' }
exec(regex, fn, string)
Calls fn
with the result of regex.exec(string)
if that result is not null,
and returns the resulting value.
parseInt(string)
Parse to integer without having to worry about the radix parameter,
making it suitable, for example, to use in array.map(parseInt)
.
slugify(string)
Replaces any series of non-word characters with a '-'
and lowercases the rest.
slugify('Party on #mydudes!') // 'party-on-mydudes'
toCamelCase(string)
Capitalises any Letter following a '-'
and removes the dash.
toPlainText(string)
Normalises string as plain text without accents using canonical decomposition (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/normalize).
wrap(min, max, n)
gaussian()
Generate a random number with a gaussian distribution centred at 0 with limits -1 to 1.
todB(level)
Converts a value to decibels relative to unity (dBFS).
toLevel(dB)
Converts a dB value relative to unity (dBFS) to unit value.
toRad(deg)
toDeg(rad)
clamp(min, max, n)
mod(divisor, n)
JavaScript’s modulu operator (%
) uses Euclidean division, but for
stuff that cycles through 0 the symmetrics of floored division are often
are more useful. This function implements floored division.
gcd(a, b)
Returns the greatest common divider of a and b.
lcm(a, b)
Returns the lowest common multiple of a and b.
factorise(array)
Reduces a fraction (represented by array
in the form
[numerator, denominator]
) by finding the greatest common divisor and
dividing by it both values by it.
Returns a new array in the form [numerator, denominator]
.
cubicBezier(point1, point2, duration, x)
Where point1
and point2
are [x, y]
vectors describing control points.
toCartesian(polar)
toPolar(cartesian)
by(fn, a, b)
Compares fn(a)
against fn(b)
and returns -1
, 0
or 1
. Useful for sorting
objects by property:
[{id: '2'}, {id: '1'}].sort(by(get('id'))); // [{id: '1'}, {id: '2'}]
byAlphabet(a, b)
Compares a
against b
alphabetically using the current locale alphabet.
each(fn, array)
Calls fn
for each member in array
.
map(fn, object)
Delegates to object.map
or Array.map
to return a new collection of mapped
values.
filter(fn, object)
Delegates to object.filter
or Array.filter
to return a new collection of
filtered objects.
reduce(fn, seed, object)
Delegates to object.reduce
or Array.reduce
to return a reduced value.
concat(array2, array1)
Where JavaScript’s Array.concat only works reliably on arrays, concat
will glue together any old array-like object.
insert(fn, array, object)
Inserts object
into array
at the first index where the result of
fn(object)
is greater than fn(array[index])
.
last(array)
Gets the last value from an array.
remove(array, value)
Remove value
from array
. Where value
is not in array
, does nothing.
rest(n, array)
take(n, array)
unique(array)
Takes an array or stream as array
, returns an object of the same
type without duplicate values.
update(create, destroy, fn, target, source)
Returns a new array containing items that are either matched objects from
target
assigned new data from source
objects or, where no match is found,
new objects created by calling create
on a source
object. Any objects
in target
that are not matched to source
objects are destroyed by calling
destroy
on them.
parseDate(date)
Parse a date, where, date
may be:
Returns a date object (or the date object, if it represents a valid date).
parseDateLocal(date)
As parseDate(date)
, but returns a date object with local time set to the
result of the parse.
formatDate(format, locale, timezone, date)
Formats date
, an ISO string or number in seconds or a JS date object,
to the format of the string format
. The format string may contain the tokens:
'YYYY'
years'YY'
2-digit year'MM'
month, 2-digit'MMM'
month, 3-letter'MMMM'
month, full name'D'
day of week'DD'
day of week, two-digit'DDD'
weekday, 3-letter'DDDD'
weekday, full name'hh'
hours'mm'
minutes'ss'
secondsThe locale
string may be 'en'
or 'fr'
. The 'timezone'
parameter is
either 'UTC'
or an IANA timezone such as ‘Europe/Zurich
‘
(timezones on Wikipedia).
const date = formatDate('YYYY', 'en', 'UTC', new Date()); // 2020
formatDateLocal(format, locale, date)
As formatDate(date)
, but returns a date object with local time set to the
result of the parse.
formatDateISO(date)
Formats date
(a string or a number or date accepted by parseDate(date)
) as
a string in the ISO date format.
formatDateTimeISO(date)
Formats date
(a string or a number or date accepted by parseDate(date)
) as
a string in the ISO datetime format.
addDate(diff, date)
Sums diff
and date
, where diff
is a string in ISO date format. Returns
a new date object.
const addWeek = addDate('0000-00-07');
const sameTimeNextWeek = addWeek(new Date());
floorDate(token, date)
Floors date to the start of nearest calendar point in increment indicated
by token
:
'Y'
Year'M'
Month'w'
Week'd'
Day'h'
Hour'm'
Minute's'
Second'mon'
Monday'tue'
Tuesday'wed'
Wednesday'thu'
Thursday'fri'
Friday'sat'
Saturday'sun'
Sundayconst dayCounts = times.map(floorDate('d'));
parseTime(time)
Where time
is a string it is parsed as a time in ISO time format: as
hours '13'
, with minutes '13:25'
, with seconds '13:25:14'
or with
decimal seconds '13:25:14.001'
. Returns a number in seconds.
const time = parseTime('13:25:14.001'); // 48314.001
Where time
is a number it is assumed to represent a time in seconds
and is returned directly.
const time = parseTime(60); // 60
formatTime(format, time)
Formats time
, an ‘hh:mm:ss’ time string or a number in seconds, to match
format
, a string that may contain the tokens:
'±'
Sign, renders ‘-‘ if time is negative, otherwise nothing'Y'
Years, approx.'M'
Months, approx.'MM'
Months, remainder from years (max 12), approx.'w'
Weeks'ww'
Weeks, remainder from months (max 4)'d'
Days'dd'
Days, remainder from weeks (max 7)'h'
Hours'hh'
Hours, remainder from days (max 24), 2-digit format'm'
Minutes'mm'
Minutes, remainder from hours (max 60), 2-digit format's'
Seconds'ss'
Seconds, remainder from minutes (max 60), 2-digit format'sss'
Seconds, remainder from minutes (max 60), fractional'ms'
Milliseconds, remainder from seconds (max 1000), 3-digit formatconst time = formatTime('±hh:mm:ss', 3600); // 01:00:00
formatTimeISO(time)
Formats time
, an ‘hh:mm:sss’ time string or a number in seconds, as a string
in the ISO time format.
`
addTime(time1, time2)
Sums time2
and time1
, which may be ‘hh:mm:sss’ time strings or numbers in
seconds, and returns time as a number in seconds. time1
may contain hours
outside the range 0-24 or minutes or seconds outside the range 0-60. For
example, to add 75 minutes to a list of times you may write:
const laters = times.map(addTime('00:75'));
floorTime(token, time)
Floors time to the start of the nearest token
, where token
is one of:
'w'
Week'd'
Day'h'
Hour'm'
Minute's'
Second'ms'
Millisecondtime
may be an ISO time string or a time in seconds. Returns a time in seconds.
const hourCounts = times.map(floorTime('h'));
An observer is an ES6 Proxy of an object that intercepts and reports mutations. Import and create an observer:
import { Observer, observe } from '/fn/module.js';
const data = Observer({ value: true });
There is only ever one observer per object, ie, calling
Observer(object)
multiple times with the same object
(or proxy of that object) always returns the same observer proxy.
Observers can be observed for mutations with the observe function:
observe('value', console.log, data);
Mutations made to the observer proxy's value
property are now logged to the console.
data.value = false; // Logs `false`
observe(path, fn, object [, init])
Observe path
in object
and call fn(value)
with the value at the
end of that path when it mutates. Returns a function that destroys this
observer.
The callback fn
is called immediately on initialisation if the value at
the end of the path is not equal to init
. In the default case where
init
is undefined
, paths that end in undefined
do not cause the
callback to be called.
(To force the callback to always be called on setup, pass in NaN
as an
init
value. In JS NaN
is not equal to anything, even NaN
, so it
always initialises.)
Import and create a stream:
import { Stream } from '/fn/module.js';
const stream = Stream.of(1,2,3,4);
.status
Reflects the running status of the stream. When all values have been consumed
status is 'done'
.
.push(value)
Pushes a value
(or multiple values) into the head of a writeable stream.
If the stream is not writeable, it does not have a .push()
method.
.flat()
Flattens a stream of streams or arrays into a single stream.
.flatMap(fn)
Maps values to lists – fn(value)
must return an array, functor, stream
(or any other duck with a .shift()
method) and flattens those lists into a
single stream.
.map(fn)
Maps values to the result of fn(value)
.
.merge(stream)
Merges this stream with stream
, which may be an array, array-like
or functor.
.scan(fn, seed)
Calls fn(accumulator, value)
and emits accumulator
for each value
in the stream.
.dedup()
Filters out consecutive equal values.
.filter(fn)
Filter values according to the truthiness of fn(value)
.
.latest()
When the stream has a values buffered, passes the last value in the buffer.
.rest(n)
Filters the stream to the n
th value and above.
.take(n)
Filters the stream to the first n
values.
.throttle(time)
Throttles values such that the latest value is emitted every time
seconds.
Other values are discarded. The parameter time
may also be a timer options
object, an object with { request, cancel, now }
functions,
allowing the creation of, say, and animation frame throttle.
.wait(time)
Emits the latest value only after time
seconds of inactivity.
Other values are discarded.
.combine(fn, stream)
Combines the latest values from this stream and stream
via the combinator
fn
any time a new value is emitted by either stream.
.clone()
Creates a read-only copy of the stream.
.each(fn)
Thirstilly consumes the stream, calling fn(value)
whenever
a value is available.
.last(fn)
Consumes the stream when stopped, calling fn(value)
with the
last value read from the stream.
.fold(fn, accumulator)
Consumes the stream when stopped, calling fn(accumulator, value)
for each value in the stream. Returns a promise.
.shift()
Reads a value from the stream. If no values are in the stream, returns
undefined
. If this is the last value in the stream, stream.status
is 'done'
.
.done(fn)
Calls fn()
after the stream is stopped and all values have been drained.
.start()
If the stream’s producer is startable, starts the stream.
.stop()
Stops the stream. No more values can be pushed to the stream and any
consumers added will do nothing. However, depending on the stream’s source
the stream may yet drain any buffered values into an existing consumer
before entering 'done'
state. Once in 'done'
state a stream is
entirely inert.