1. Synopsis
2. Installation
3. How To Use
4. Available Methods
4.1. Array
4.2. Functional
4.3. Iterable
4.4. Misc
4.5. Promise
4.6. String
4.7. Value
5. Testing
6. Contributions
6.1. Change Notes
7. License
A one stop shop for functional utility needs. This project was designed for me to have a place to get the kinds of functions I use a lot. This is designed to evolve over time.
Many of these functions are existing methods in javascript, written to accept the context as the first parameter. Some functions, such as map
and reduce
have been extended to work with strings and objects.
Install with npm as a dependency to your project.
$ npm i --save fun-util
const {
string: { upperCase },
functional: { ifn, identity },
iterable: { map }
} = require('fun-util');
const isIndexDivisibleByTwo = (_, index) => number % 2 === 0;
map('this is a string', ifn(isIndexDivisibleByTwo, upperCase, identity));
// => 'ThIs iS A StRiNg'
Methods related to arrays.
const { array } = require('fun-util');
Object.keys(array);
// => ['join', 'range', 'toArray']
The Array.prototype
method that defaults to no character between items instead of a comma.
const { join } = require('fun-util');
join([1, 2, 3, 4, 'apple']);
// => '1234apple'
Creates an array from the starting number (inclusive) to the ending number (exclusive) with an optional step.
const { range } = require('fun-util');
range(10);
// => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
range(1, -12);
// => [1, 0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11]
range(0, 12, 3);
// => [0, 3, 6, 9]
Returns the passed parameters as an array
.
const { toArray } = require('fun-util');
toArray(1, 2, 3);
// => [1, 2, 3]
toArray([1, 2, 3]);
// => [[1, 2, 3]]
Basic operations that either accept a function, return a function, or do not care what type of value it receives.
const { functional } = require('fun-util');
Object.keys(functional);
// => ['apply', 'complement', 'compose', 'enforceTypes', 'identity', 'ifn', 'memoize', 'overload', 'partial', 'partialReverse', 'silent', 'thread', 'through', 'truncateArgs']
Applies an array to a function as individual arguments.
const { apply } = require('fun-util');
apply(a => a, [1, 2, 3]);
// => 1
Creates a function that returns the negated value of the function supplied.
const { complement } = require('fun-util');
const isThree = x => x === 3;
const notThree = complement(isThree);
isThree(3);
// => true
notThree(3);
// => false
Creates a function that takes inputs and passes them to last function supplied and then passes the return value to the previous function and so forth, returning the final result.
const { compose } = require('fun-util');
const fn = compose(f, g, h);
fn(3);
// => f(g(h(3)));
Throws an error if the types passed into the function do no match the type list. The value null
matches any type.
const { enforceTypes } = require('fun-util');
class MyType {}
const myType = new MyType;
const fn = enforceTypes(Number, [String], MyType, (number, stringArray, myType) => {
return 'this works';
}).theRestAre(Function);
fn(1, ['a', 'b', 'c'], myType);
// => 'this works'
fn(null, [], null, () => 'apples', () => 'oranges');
// => 'this works'
fn(0, ['string']);
// => TypeError: Expected "undefined" to be of Type "MyType"
fn(null, null, null, 'not a function', () => 'function');
// => TypeError: Expected "not a function" to be of Type "Function"
Returns the value supplied and ignores extra arguments.
const { identity } = require('fun-util');
identity(17, 3);
// => 17
Creates a function with a testFn, successFn, and failFn. The function then takes inputs. If the result of passing those inputs through testFn are truthy, it returns the value of passing those inputs to successFn. Otherwise it returns the value of passing those inputs to failFn.
const { ifn } = require('fun-util');
const condition = (a, b, c) => a === b && a === c;
const successFn = (a, b, c) => a + b + c;
const failFn = (a, b, c) => a - b - c;
const fn = ifn(condition, successFn, failFn);
fn(1, 2, 3);
// => -4
fn(4, 4, 4);
// => 12
Memoizes a function and returns cached results on subsequent calls with the same input.
const { memoize } = require('fun-util');
const memoizedProceedure = memoize(complicatedProceedure);
memoizedProceedure(impossiblyComplicatedInput);
// => Process Completed in 5,000 earth years.
memoizedProceedure(impossiblyComplicatedInput);
// => Process Completed in 0.0013 miliseconds.
Enforces arity and simulates method overloading. This uses the length
of the function which ignores the "rest" parameter and any arguments with default values.
((a, ...args) => {}).length
// => 1
((a, b, c = {}, d = 14) => {}).length
// => 2
See MDN for more information.
Add an orElse
clause to overwrite default error throwing.
const { overload, toArray } = require('fun-util');
const addNumbers = (a, b) => a + b;
const addTwo = a => add(a, 2);
const add = overload(addNumbers, addTwo);
const addOrElse = overload(addNumbers, addTwo).orElse(toArray);
add(2);
// => 4
add(1, 7);
// => 8
add(1, 2, 3, 4);
// => Error: ArityMismatch: No function found with 4 argument(s).
addOrElse(1, 2, 3, 4);
// => [1, 2, 3, 4]
Partially applies a function.
const { partial } = require('fun-util');
const infoLogger = partial(console.log, 'INFO:');
infoLogger('Hello, world!');
// => INFO: Hello, world!
Applies initial arguments after the subsequent arguments.
const { last, map, partialReverse, toArray } = require('fun-util');
const hallOfMirrors = partialReverse(map, toArray, last);
hallOfMirrors([1, 2, 3]);
// => map([1, 2, 3], toArray, last);
// => [[1, 2, 3], [1, 2, 3], [1, 2, 3]]
Applies initial arguments after the subsequent arguments.
const { silent } = require('fun-util');
const parseJSON = silent(JSON.parse);
parseJSON('{"some":"data"}');
// => { some: 'data' }
parseJSON('not {{json}}');
// => 'not {{json}}'
Similar to compose, creates a function threads the response of the first function through to the next and so on.
const { thread } = require('fun-util');
const fn = thread(addOne, double, addOne);
fn(7);
// => 17
Returns a function that passes arguments through a function but returns the original input.
const { map, through } = require('fun-util');
map([1, 2, 3], console.log, number => number * 2);
// 1 0 [ 1, 2, 3 ]
// 2 1 [ 1, 2, 3 ]
// 3 2 [ 1, 2, 3 ]
// => [ NaN, NaN, NaN ]
map([1, 2, 3], through(console.log), number => number * 2);
// 1 0 [ 1, 2, 3 ]
// 2 1 [ 1, 2, 3 ]
// 3 2 [ 1, 2, 3 ]
// => [2, 4, 6]
Returns a function that limits the number of args passed to the suplied function.
const { forEach, truncateArgs } = require('fun-util');
forEach(['a', 'b', 'c'], console.log);
// a 0 ['a', 'b', 'c']
// b 1 ['a', 'b', 'c']
// c 2 ['a', 'b', 'c']
forEach(['a', 'b', 'c'], truncateArgs(console.log, 1));
// a
// b
// c
Methods that apply to iterable objects, arrays, and strings.
const { iterable } = require('fun-util');
Object.keys(iterable);
// => ['any', 'concat', 'every', 'filter', 'find', 'first', 'firstRest', 'flatMap', 'flatten', 'forEach', 'groupBy', 'hasKey', 'last', 'map', 'mapAllKeys', 'mapFilter', 'reduce', 'rest', 'reverse', 'size', 'sort', 'splitWhen', 'takeUntil', 'takeWhile', 'truncate', 'truncateLast']
The Array.prototype
method adapted to work with strings and objects.
const { any } = require('fun-util');
any({ a: 1, b: 2, c: '3' }, value => value === String(value));
// => true
The Array.prototype
method adapted to work with strings and objects.
const { concat } = require('fun-util');
concat({ a: 1 }, { b: 2 });
// => { a: 1, b: 2 }
The Array.prototype
method adapted to work with strings and objects.
const { every } = require('fun-util');
every('asdfghj0', character => character.match(/[a-z]/));
// => false
The Array.prototype
method adapted to work with strings and objects.
const { filter } = require('fun-util');
filter({ a: 1, b: 2, c: 'c' }, Number);
// => { a: 1, b: 2 }
The Array.prototype
method adapted to work with strings and objects. Note that finding something in an object may return varying results as the order of keys being iterated over is not guaranteed.
const { find } = require('fun-util');
find('asdfghjk', character => character.match(/[A-Z]/));
// => undefined
Returns the first element of an array or string.
const { first } = require('fun-util');
first([1, 2, 3]);
// => 1
Combines first
and rest
for convenience.
const { firstRest } = require('fun-util');
let [item, items] = firstRest([1, 2, 3, 4, 5]);
item
// => 1
items
// => [2, 3, 4, 5]
This flattens a 2 or more dimensional array and maps the result through mapping functions if provided.
const { flatMap } = require('fun-util');
flatMap([[1], [[2], 3], 4]);
// => [1, 2, 3, 4]
flatMap([[1], [[2], 3], 4], number => number * 2, number => number + 3);
// => [5, 7, 9, 11]
Flattens a multi-dimensional array. It completely falttens by default or to a specified depth.
const { flatten } = require('fun-util');
flatten([1, [2, [3, [4, [5, [6]]]]]]);
// => [1, 2, 3, 4, 5, 6]
flatten([1, [2, [3, [4, [5, [6]]]]]], 2);
// => [1, 2, 3, [4, [5, [6]]]]
The Array.prototype
method adapted to work with strings and objects. Takes multiple items and multiple functions.
const { forEach } = require('fun-util');
forEach('123' [4, 5, 6], (digit1, digit2) => {
console.log(digit1, digit2);
});
// => '1 4'
// => '2 5'
// => '3 6'
Splits an object, array, or string by a grouping function and produces an object keyed off the different values the grouping function returns.
const { groupBy } = require('fun-util');
groupBy([1, 2, 3, 4, 5, 6, 7, 8], number => number % 2 ? 'odd' : 'even');
// => { odd: [1, 3, 5, 7], even: [2, 4, 6, 8] }
The existing hasOwnProperty
method for strings, arrays, and objects.
const { hasKey } = require('fun-util');
hasKey([1, 2, 3, 4], 2);
// => true
hasKey('apples', 17);
// => false
Returns the last element of an array or string.
const { last } = require('fun-util');
last([1, 2, 3]);
// => 3
The Array.prototype
method adapted to work with strings and objects. It accepts multiple items and multiple mapping functions. It maps to the type of the first supplied value.
const { map } = require('fun-util');
map({ a: 1, b: 2, c: 3 }, addOne, double, addOne);
// => { a: 5, b: 7, c: 9 }
Unlike map
which only iterates over the first value's keys, mapAllKeys
iterates over every unique key of all values. Like map
, mapAllKeys
returns the type of the first supplied value. When mapping to a string or an array, keys which are not whole numbers are dropped on the floor in the result.
const { mapAllKeys } = require('fun-util');
mapAllKeys({ a: 1, b: 2}, [0, 1, 2], 'abc', (value1, value2, value3) => value1 || value2 || value3);
// => { a: 1, b: 2, '0': 'a', '1': 1, '2': 2 }
A combination of map
and filter
. If the map-filtering function returns undefined
, the value is filtered
out. Otherwise it is mapped to the new output. This works with objects, arrays, and strings.
const { mapFilter } = require('fun-util');
mapFilter({ a: 1, b: 2, c: 3 }, (value, key) => {
if (key !== 'b') {
return value - 1;
}
});
// => { a: 0, c: 2 }
The Array.prototype
method adapted to work with strings and objects.
const { reduce } = require('fun-util');
reduce({ a: 1, b: 2, c: 3 }, [4, 5, 6], (total, value1, value2) => total + value1 + value2);
// => 21
Returns a new array or string with the first element removed.
const { rest } = require('fun-util');
rest('value');
// => 'alue'
Returns a new array (not mutative) or string with the values reversed.
const { reverse } = require('fun-util');
const array = [1, 2, 3];
reverse(array);
// => [3, 2, 1]
array;
// => [1, 2, 3]
The length of a string or array, or the number of an object's keys.
const { size } = require('fun-util');
size('123456');
// => 6
Returns a new array (not mutative) or string with the values sorted. It takes a method, but by default sorts by >
and <
comparisons.
const { sort } = require('fun-util');
const array = [10, 3, 2 ,1];
sort(array);
// => [1, 2, 3, 10]
array;
// => [10, 3, 2, 1]
Splits an array or string starting the second item when an element meets the criteria.
const { splitWhen } = require('fun-util');
splitWhen('abcdEFGH', letter => letter === letter.toUpperCase());
// => ['abcd', 'EFGH']
Takes each element in an array or string until the supplied callback returns a truthy value.
const { takeUntil } = require('fun-util');
takeUntil([1, 2, 3, 4, 5], number => number > 3);
// => [1, 2, 3]
Takes each element in an array or string until the supplied callback returns a falsey value.
const { takeWhile } = require('fun-util');
takeWhile('this is a string', character => character.match(/\w/));
// => 'this'
Return a slice array (not mutative) or string with the last element removed.
const { truncate } = require('fun-util');
truncate([1, 2, 3, 4, 5]);
// => [1, 2, 3, 4]
Combines truncate
and last
for convenience.
const { truncateLast } = require('fun-util');
let [items, item] = truncateLast([1, 2, 3, 4, 5]);
items
// => [1, 2, 3, 4]
item
// => 5
Useful miscellaneous methods.
const { misc } = require('fun-util');
Object.keys(misc);
// => ['deepCompare', 'deepCopy', 'deepEqual', 'deepMerge', 'getIn', 'slice', 'updateIn']
Generates a representation of all differences when comparing two values. This was designed more for debugging purposes.
const { deepCompare } = require('fun-util');
const object1 = {
list: [1, 2, 3],
data: {
favoriteNumber: -11
},
favoriteColor: 'blue'
};
const object2 = {
list: [1, 2, 3, 4],
data: {
favoriteNumber: -11
},
favoriteColor: 'yellow'
};
deepCompare(object1, object2);
// => { list: { '3': 'undefined != 4' }, favoriteColor: '"blue" != "yellow"' }
Preforms a deep copy of all nested objects and arrays. Functions are still copied by reference.
const { deepCopy } = require('fun-util');
const x = { a: 1, b: { c: [1, 2, 3], d() { return this.c } } };
const y = deepCopy(x);
x.b.c.push(4);
x.b.d();
// => [1, 2, 3, 4]
y.b.d();
// => [1, 2, 3]
Preforms a deep comparison of all nested objects, arrays, and functions.
const { deepEqual } = require('fun-util');
const x = { a: 1, b: ['apple', () => null] };
const y = { a: 1, b: ['apple', () => null] };
deepEqual(x, y);
// => true
Merges two values with right-hand precedence.
const { deepMerge } = require('fun-util');
deepMerge({ a: 1, b: [{ c: 2, d: 3 }, 'old string'] }, { e: 4, b: [{ c: 5 }, 'new string'] });
// => { a: 1, b: [{ c: 5, d: 3 }, 'new string'], e: 4 }
Gets a nested value from a complex object or returns undefined.
const { getIn } = require('fun-util');
getIn({ a: { b: [{ c: 17 }] } }, 'a', 'b', 0, 'c');
// => 17
getIn({ a: 1 }, 'b', 'c', 3, 'd');
// undefined
The existing slice
method for strings and arrays in "call" form.
const { slice } = require('fun-util');
slice([1, 2, 3, 4], 2);
// => [3, 4]
Copies and sets nested value where the first argument is the starting data structure, the last argument is the value to set, and the remaining arguments identify the location to set the value. Creates nested arrays and objects as needed.
const { updateIn } = require('fun-util');
updateIn({ x: 'x' }, 'a', 0, 'b', { c: 17 });
// => { a: [{ b: { c: 17 } }], x: 'x' }
Helpers built on the standard Promise library.
const { promise } = require('fun-util');
Object.keys(promise);
// => ['asyncWhile', 'chain', 'rejectThrough', 'resolveThrough', 'sleep']
Takes a promise and a function to be run repeatedly until the promise resolves. This returns a promise that resolves or rejects the same data/error resolved or rejected by the supplied promise. Note that the function is run using setInterval
. For more on how that will be run, see documentation for NodeJS.
const { asyncWhile } = require('fun-util');
asyncWhile(fetchApiData(), updateProgressBar)
.then(processApiData)
.catch(handleApiError);
Chain promises that run in the order provided. A simpler way to write a promise chain if your promises are order dependent but do not require data from each other.
const { chain } = require('fun-util');
const reseedTestData() => {
return chain([
() => sql('DELETE * FROM TABLE'),
() => sql('DELETE * FROM OTHER TABLE'),
() => sql('INSERT INTO TABLE...'),
() => sql('INSERT INTO OTHER TABLE...')
]);
};
A version of through
that works with promises. This rejects the data passed through it.
const { rejectThrough } = require('fun-util');
doSomethingImportant()
.catch(rejectThrough(writeToLogFile))
.then(finishSuccessfully, finishUnsuccessfully);
// => finishUnsuccessfully(error)
A version of through
that works with promises. This resolves the data passed through it.
const { resolveThrough } = require('fun-util');
doSomethingImportant()
.then(resolveThrough(debugLog))
.then(finishSuccessfully, finishUnsuccessfully);
// => finishSuccessfully(data)
A promise wrapper around setTimeout
. For more on how that will be run, see documentation for NodeJS.
const { sleep } = require('fun-util');
sleep(1000).then(() => console.log('it has been (at least) one second'));
Methods that act on strings.
Several string methods have also been duplicated in call form to make them easier to use as mapping methods.
These include: lowerCase
, match
, replace
, trim
, and upperCase
.
const { string } = require('fun-util');
Object.keys(string);
// => ['lowerCase', 'match', 'replace', 'split', 'trim', 'upperCase']
The split string method has been wrapped to split on each character by default.
const { split } = require('fun-util');
'string'.split();
// => ['string']
split('string');
// => ['s', 't', 'r', 'i', 'n', 'g']
split('string', 't');
// => ['s', 'ring']
Functions that qualify an input's value.
const { string } = require('fun-util');
Object.keys(string);
// => ['isEmpty', 'isEqual', 'isNaN', 'isNothing', 'type']
Returns true
or false
for any values based on their "truthiness" or "falsiness". Also returns false
for empty arrays or empty objects.
const { isEmpty } = require('fun-util');
isEmpty([]);
// => true
isEmpty([1, 2, 3]);
// => false
Returns the direct equality comparison of two inputs. The only difference between isEqual
and ===
is that isEqual
recognizes that NaN
and NaN
are equal.
const { isEqual } = require('fun-util');
isEqual(NaN, NaN);
// true
isEqual({}, {});
// false
An improved isNaN
function that doesn't recognize undefined
as being NaN
.
const betterIsNaN = require('fun-util').isNaN;
isNaN(undefined);
// true
betterIsNaN();
// false
Returns true
for null
and undefined
. Returns false
for all other values.
const { isNothing } = require('fun-util');
isNothing(false);
// => false
isNothing(null);
// => true
An improved version of typeof
which differentiates between array
and object
.
const { type } = require('fun-util');
typeof [1, 2, 3];
// => 'object'
type([1, 2, 3]);
// => 'array'
Tests are written using jasmine.
$ npm i
$ npm test
Fun-Util is open source. Contribute today at http://www.github.com/skuttleman/fun-util!
- Add iterable/groupBy
- Add misc/deepCompare and misc/deepMerge
- Add iterable/mapAllKeys
- Add value/isEqual and value/isNaN
- Move misc/type to value/type
- Add promise/sleep and promise/asyncWhile
- Add value/isEmpty and value/isNothing
- Improve misc/updateIn performance and tests
- Add functional/truncateArgs
- Update iterable/flatten to flatten to a specified deptha
- Add iterable/flatten
- Fix bug with iterable/reduce when calling on empty collection
- Add functional/silent
- Add functional/enforceTypes
- Add iterable/splitWhen
- iterable/forEach, iterable/map, and iterable/reduce take multiple items, and iterable/forEach takes multiple functions
- Add 'iterable/firstRest' 'iterable/flatMap', 'iterable/truncateLast', 'misc/type', and 'misc/updateIn'
- Add error output when transpiling fails
- Add 'iterable/takeWhile' and 'iterable/takeUntil'
- Create shell script to update change log from commit messages
- Add .orElse method to overload.
- Add overload to functional methods.
- Add mapFilter to iterable methods.
- Transpile to ES5 before publishing module.
- Add Promise methods.
- All methods are optionally accessible by name without referencing the group.
const { iterable, forEach } = require('fun-util');
iterable.forEach === forEach;
// => true
ISC Lisense
Copyright (c)2016, Ben Allred [email protected]
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.