fun-util
v1.2.0
Published
A collection of reusable helper methods for a variety of occasions.
Downloads
257
Readme
Fun-Util
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
1. Synopsis
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.
2. Installation
Install with npm as a dependency to your project.
$ npm i --save fun-util
3. How To Use
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'
4. Available Methods
4.1. Array
Methods related to arrays.
All Array Methods
const { array } = require('fun-util');
Object.keys(array);
// => ['join', 'range', 'toArray']
-join
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'
-range
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]
-toArray
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]]
4.2. Functional
Basic operations that either accept a function, return a function, or do not care what type of value it receives.
All Functional Methods
const { functional } = require('fun-util');
Object.keys(functional);
// => ['apply', 'complement', 'compose', 'enforceTypes', 'identity', 'ifn', 'memoize', 'overload', 'partial', 'partialReverse', 'silent', 'thread', 'through', 'truncateArgs']
-apply
Applies an array to a function as individual arguments.
const { apply } = require('fun-util');
apply(a => a, [1, 2, 3]);
// => 1
-complement
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
-compose
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)));
-enforceTypes
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"
-identity
Returns the value supplied and ignores extra arguments.
const { identity } = require('fun-util');
identity(17, 3);
// => 17
-ifn
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
-memoize
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.
-overload
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]
-partial
Partially applies a function.
const { partial } = require('fun-util');
const infoLogger = partial(console.log, 'INFO:');
infoLogger('Hello, world!');
// => INFO: Hello, world!
-partialReverse
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]]
-silent
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}}'
-thread
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
-through
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]
-truncateArgs
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
4.3. Iterable
Methods that apply to iterable objects, arrays, and strings.
All Iterable methods
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']
-any
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
-concat
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 }
-every
The Array.prototype
method adapted to work with strings and objects.
const { every } = require('fun-util');
every('asdfghj0', character => character.match(/[a-z]/));
// => false
-filter
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 }
-find
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
-first
Returns the first element of an array or string.
const { first } = require('fun-util');
first([1, 2, 3]);
// => 1
-firstRest
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]
-flatMap
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]
-flatten
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]]]]
-forEach
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'
= groupBy
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] }
-hasKey
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
-last
Returns the last element of an array or string.
const { last } = require('fun-util');
last([1, 2, 3]);
// => 3
-map
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 }
-mapAllKeys
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 }
-mapFilter
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 }
-reduce
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
-rest
Returns a new array or string with the first element removed.
const { rest } = require('fun-util');
rest('value');
// => 'alue'
-reverse
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]
-size
The length of a string or array, or the number of an object's keys.
const { size } = require('fun-util');
size('123456');
// => 6
-sort
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]
-splitWhen
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']
-takeUntil
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]
-takeWhile
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'
-truncate
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]
-truncateLast
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
4.4. Misc
Useful miscellaneous methods.
All Misc methods
const { misc } = require('fun-util');
Object.keys(misc);
// => ['deepCompare', 'deepCopy', 'deepEqual', 'deepMerge', 'getIn', 'slice', 'updateIn']
-deepCompare
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"' }
-deepCopy
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]
-deepEqual
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
-deepMerge
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 }
-getIn
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
-slice
The existing slice
method for strings and arrays in "call" form.
const { slice } = require('fun-util');
slice([1, 2, 3, 4], 2);
// => [3, 4]
- updateIn
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' }
4.5. Promise
Helpers built on the standard Promise library.
All Promise methods
const { promise } = require('fun-util');
Object.keys(promise);
// => ['asyncWhile', 'chain', 'rejectThrough', 'resolveThrough', 'sleep']
-asyncWhile
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
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...')
]);
};
-rejectThrough
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)
-resolveThrough
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)
-sleep
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'));
4.6. String
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
.
All String methods
const { string } = require('fun-util');
Object.keys(string);
// => ['lowerCase', 'match', 'replace', 'split', 'trim', 'upperCase']
-split
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']
4.7. Value
Functions that qualify an input's value.
All Value methods
const { string } = require('fun-util');
Object.keys(string);
// => ['isEmpty', 'isEqual', 'isNaN', 'isNothing', 'type']
-isEmpty
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
-isEqual
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
-isNaN
An improved isNaN
function that doesn't recognize undefined
as being NaN
.
const betterIsNaN = require('fun-util').isNaN;
isNaN(undefined);
// true
betterIsNaN();
// false
-isNothing
Returns true
for null
and undefined
. Returns false
for all other values.
const { isNothing } = require('fun-util');
isNothing(false);
// => false
isNothing(null);
// => true
-type
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'
5. Testing
Tests are written using jasmine.
$ npm i
$ npm test
6. Contributions
Fun-Util is open source. Contribute today at http://www.github.com/skuttleman/fun-util!
6.1. Change Notes
1.2.0
- Add iterable/groupBy
1.1.0
- Add misc/deepCompare and misc/deepMerge
- Add iterable/mapAllKeys
- Add value/isEqual and value/isNaN
1.0.0
- 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
0.13.1
- Update iterable/flatten to flatten to a specified deptha
0.13.0
- Add iterable/flatten
0.12.1
- Fix bug with iterable/reduce when calling on empty collection
0.12.0
- Add functional/silent
0.11.0
- Add functional/enforceTypes
0.10.0
- Add iterable/splitWhen
- iterable/forEach, iterable/map, and iterable/reduce take multiple items, and iterable/forEach takes multiple functions
0.9.0
- Add 'iterable/firstRest' 'iterable/flatMap', 'iterable/truncateLast', 'misc/type', and 'misc/updateIn'
- Add error output when transpiling fails
0.8.0
- Add 'iterable/takeWhile' and 'iterable/takeUntil'
- Create shell script to update change log from commit messages
0.7.0
- Add .orElse method to overload.
0.6.0
- Add overload to functional methods.
0.5.0
- Add mapFilter to iterable methods.
0.4.2
- Transpile to ES5 before publishing module.
0.4.0
- Add Promise methods.
0.2.0
- All methods are optionally accessible by name without referencing the group.
const { iterable, forEach } = require('fun-util');
iterable.forEach === forEach;
// => true
7. License
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.