Skip to content

Instantly share code, notes, and snippets.

@bendc
Last active September 15, 2023 12:12
Show Gist options
  • Save bendc/9b05735dfa6966859025 to your computer and use it in GitHub Desktop.
Save bendc/9b05735dfa6966859025 to your computer and use it in GitHub Desktop.
A set of pure ES2015 functions aimed to make functional JavaScript more idiomatic.
// array utils
// =================================================================================================
const combine = (...arrays) => [].concat(...arrays);
const compact = arr => arr.filter(Boolean);
const contains = (() => Array.prototype.includes
? (arr, value) => arr.includes(value)
: (arr, value) => arr.some(el => el === value)
)();
const difference = (arr, ...others) => {
var combined = [].concat(...others)
return arr.filter(el => !combined.some(exclude => el === exclude))
};
const head = arr => arr[0];
const initial = arr => arr.slice(0, -1);
const intersection = (...arrays) =>
[...Set([].concat(...arrays))].filter(toFind =>
arrays.every(arr => arr.some(el => el === toFind))
);
const last = arr => arr.slice(-1)[0];
const sortedIndex = (arr, value) =>
[value].concat(arr).sort().indexOf(value);
const tail = arr => arr.slice(1);
const toArray = (() => Array.from ? Array.from : obj => [].slice.call(obj))();
const union = (...arrays) => [...Set([].concat(...arrays))];
const unique = arr => [...Set(arr)];
const without = (arr, ...values) =>
arr.filter(el => !values.some(exclude => el === exclude));
// object utils
// =================================================================================================
const getValues = obj => Object.keys(obj).map(key => obj[key]);
const merge = (() => {
const extend = Object.assign ? Object.assign : (target, ...sources) => {
sources.forEach(source =>
Object.keys(source).forEach(prop => target[prop] = source[prop])
);
return target;
};
return (...objects) => extend({}, ...objects);
})();
const toMap = (() => {
const convert = obj => new Map(Object.keys(obj).map(key => [key, obj[key]]));
return obj => obj instanceof Map ? obj : convert(obj);
})();
// math
// =================================================================================================
const min = arr => Math.min(...arr);
const max = arr => Math.max(...arr);
const sum = arr => arr.reduce((a, b) => a + b);
const product = arr => arr.reduce((a, b) => a * b);
// function decorators
// =================================================================================================
const not = fn => (...args) => !fn(...args);
const maybe = fn =>
(...args) => {
if (args.length < fn.length || args.some(arg => arg == null)) return;
return fn(...args);
};
const once = fn => {
var done = false;
return (...args) => {
if (done) return;
done = true;
fn(...args);
};
};
const curry = fn => {
const arity = fn.length;
const curried = (...args) =>
args.length < arity ? (...more) => curried(...args, ...more) : fn(...args);
return curried;
};
const pipeline = (...funcs) =>
value => funcs.reduce((a, b) => b(a), value);
@bendc
Copy link
Author

bendc commented Jan 12, 2015

combine(arrays)

Combine multiple arrays into one array.

combine(["foo"], ["bar", "baz"], [1, 2]) // => ["foo", "bar", "baz", 1, 2]

compact(array)

Returns a copy of the array with all falsy values removed.

compact([0, 1, false, 2, "", 3]) // => [1, 2, 3]

contains(array, value)

Returns true if the value is present in the array.

contains([1, 2, 3], 3) // => true

difference(array, others)

Similar to without, but returns the values from array that are not present in the other arrays.

difference([1, 2, 3, 4, 5], [5, 2, 10]) // => [1, 3, 4]

head(array)

Returns the first element of an array.

head(["foo", "bar"]) // => "foo"

initial(array)

Returns everything but the last entry of the array.

initial([3, 2, 1]) // => [3, 2]

intersection(arrays)

Computes the list of values that are the intersection of all the arrays. Each value in the result is present in each of the arrays.

intersection([1, 2, 3], [101, 2, 1, 10], [2, 1]) // => [1, 2]

last(array)

Returns the last element of an array.

last(["foo", "bar"]) // => "bar"

sortedIndex(array, value)

Determine the index at which the value should be inserted into the array in order to maintain the array's sorted order.

sortedIndex([10, 20, 30, 40, 50], 35) // => 3

tail(array)

Returns everything but the first entry of the array.

tail(["foo", "bar", "baz"]) // => ["bar", "baz"]

toArray(arrayLike)

Returns a real Array. Useful for transmuting the arguments object.

Array.isArray((() => toArray(arguments))("foo", "bar")) // => true

union(arrays)

Computes the union of the passed-in arrays: the list of unique items, in order, that are present in one or more of the arrays.

union([1, 2, 3], [101, 2, 1, 10], [2, 1]) // => [1, 2, 3, 101, 10]

unique(array)

Produces a duplicate-free version of the array.

unique([1, 2, 1, 3, 1, 4]) // => [1, 2, 3, 4]

without(array, values)

Returns a copy of the array with all instances of the values removed.

without([1, 2, 1, 0, 3, 1, 4], 0, 1) // => [2, 3, 4]

getValues(object)

Returns an array with the object's values.

getValues({ foo: "bar", hello: "world" }) // => ["bar", "world"]

merge(objects)

Combine multiple objects into a new object.

merge({ foo: "bar" }, { hello: "world" }) // => { foo: "bar", hello: "world" }

toMap(object)

Convert an Object to a Map.

toMap({ name: "Ben", age: 31 }); // => Map { name: "Ben", age: 31 }

min(array)

Returns the minimum value in the array.

min([10, 50, 30]) // => 10

max(array)

Returns the maximum value in the array.

max([10, 50, 30]) // => 50

sum(array)

Returns the sum of all values in the array.

sum([1, 2, 3]) // => 6

product(array)

Returns the product of all values in the array.

product([2, 5, 10]) // => 100

not(function)

Creates a new function returning the opposite of the function provided as its argument.

const isNull = x => x == null;
const isSet = not(isNull);
isSet(undefined); // => false

maybe(function)

Returns a new function that won't execute if not enough arguments are provided.

var greet = (message, name) => console.log(message + " " + name);
var safeGreet = requireArguments(greet);

greet("Hi"); // => "Hi undefined"
safeGreet("Hi"); // => Doesn't execute

once(function)

Returns a new function that won't execute more than once.

const greet = () => console.log("Hi");
const greetOnce = once(greet);
greetOnce(); // => "Hi"
greetOnce(); // => Doesn't execute

curry(function)

Curries a function.

const add = curry((a, b) => a + b);
add(2, 3); // => 5
add(2)(3); // => 5

pipeline(functions)

Returns the composition of multiple functions from left to right.

var plus1 = a => a + 1;
var mult2 = a => a * 2;
var addThenMult = pipeline(plus1, mult2);
addThenMult(5); // => 12

@pratttttt
Copy link

Among the functions, not sure if "initial" makes sense semantically. dropLast or butLast or pop might be a better choice.

@nfroidure
Copy link

@webbedspace
Copy link

combine seems kind of ill-written given that Array#concat has always accepted multiple arrays. What about:

combine = (...a) => [].concat(...a)

@bendc
Copy link
Author

bendc commented Jan 13, 2015

@webbedspace: You're absolutely right, just updated the code. Thanks!

@mathiasbynens
Copy link

@nfroidure That’s the identity function. noop implies a function that doesn’t do anything (not even returning a value), e.g. const noop = () => {};.

@nfroidure
Copy link

@mathiasbynens right! thanks for the correction

@goatslacker
Copy link

Why is head:

const head = arr
  => arr[0]

and last not:

const last = arr => arr[arr.length - 1]

@WebReflection
Copy link

FYI: assign should eventually be shimmed via Object.getOwnPropertySymbols which might be there, but not necessarily in browsers that support assign too.

https://github.com/WebReflection/get-own-property-symbols

@o11c
Copy link

o11c commented May 16, 2015

Most of these have horrible asymptotic performance.

@joeybaker
Copy link

compact could be shortened to:

const identity = x => x

const compact = arr => arr.filter(identity)

Or even

const compact = arr => arr.filter(Boolean)

@bendc
Copy link
Author

bendc commented May 18, 2015

@joeybaker: Cleaner indeed, updated! Thanks :)

@VictorChatterji
Copy link

you sir ! are a legend.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment