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-utilconst {
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]);
// => 1Creates 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);
// => falseCreates 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);
// => 17Creates 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);
// => 12Memoizes 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
// => 2See 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);
// => 17Returns 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
// cMethods 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));
// => trueThe 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]/));
// => falseThe 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]/));
// => undefinedReturns the first element of an array or string.
const { first } = require('fun-util');
first([1, 2, 3]);
// => 1Combines 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);
// => falseReturns the last element of an array or string.
const { last } = require('fun-util');
last([1, 2, 3]);
// => 3The 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);
// => 21Returns 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');
// => 6Returns 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
// => 5Useful 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);
// => trueMerges 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');
// undefinedThe 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]);
// => falseReturns 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({}, {});
// falseAn improved isNaN function that doesn't recognize undefined as being NaN.
const betterIsNaN = require('fun-util').isNaN;
isNaN(undefined);
// true
betterIsNaN();
// falseReturns true for null and undefined. Returns false for all other values.
const { isNothing } = require('fun-util');
isNothing(false);
// => false
isNothing(null);
// => trueAn 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 testFun-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;
// => trueISC Lisense
Copyright (c)2016, Ben Allred skuttleman@gmail.com
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.