Functions

Import functions from the Fn module:

import { get, matches } from '/fn/module.js';

Functions

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.

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.

Objects

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.

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 }));

Strings

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'

Maths

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.

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].

Arrays

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'}]

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.

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]).

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.

Time

parseDate(date)

Parse a date, where, date may be:

  • a string in ISO date format
  • a number in seconds UNIX time
  • a date object

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' seconds

The 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

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' Sunday
const 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 format
const 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' Millisecond

time may be an ISO time string or a time in seconds. Returns a time in seconds.

const hourCounts = times.map(floorTime('h'));

Observer

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.)

Stream

Import and create a stream:

import { Stream } from '/fn/module.js';
const stream = Stream.of(1,2,3,4);

Properties

.status

Reflects the running status of the stream. When all values have been consumed status is 'done'.

Write

.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.

Map

.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.

Filter

.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 nth 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.

Read

.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'.

Lifecycle

.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.