Skip to content

Instantly share code, notes, and snippets.

@daliborgogic
Last active July 22, 2018 14:13
Show Gist options
  • Save daliborgogic/d3bcfe414af3f5a13d2d126fd4aadc08 to your computer and use it in GitHub Desktop.
Save daliborgogic/d3bcfe414af3f5a13d2d126fd4aadc08 to your computer and use it in GitHub Desktop.
Functional JavaScript [idiomatic]
// Array utils /////////////////////////////////////////////////////////////////
export const combine = (...arrays) => [].concat(...arrays)
export const compact = arr => arr.filter(Boolean)
export const contains = (() => Array.prototype.includes
? (arr, value) => arr.includes(value)
: (arr, value) => arr.some(el => el === value)
)()
export const difference = (arr, ...others) => {
var combined = [].concat(...others)
return arr.filter(el => !combined.some(exclude => el === exclude))
}
export const head = arr => arr[0]
export const initial = arr => arr.slice(0, -1)
export const intersection = (...arrays) =>
[...new Set([].concat(...arrays))].filter(toFind =>
arrays.every(arr => arr.some(el => el === toFind))
)
export const last = arr => arr.slice(-1)[0]
export const sortedIndex = (arr, value) =>
[value].concat(arr).sort().indexOf(value)
export const tail = arr => arr.slice(1)
export const toArray = (() => Array.from ? Array.from : obj => [].slice.call(obj))()
export const union = (...arrays) => [...Set([].concat(...arrays))]
export const unique = arr => [...Set(arr)]
export const without = (arr, ...values) =>
arr.filter(el => !values.some(exclude => el === exclude))
// Object utils ////////////////////////////////////////////////////////////////
export const getValues = obj => Object.keys(obj).map(key => obj[key])
export 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)
})()
export const toMap = (() => {
const convert = obj => new Map(Object.keys(obj).map(key => [key, obj[key]]))
return obj => obj instanceof Map ? obj : convert(obj)
})()
// Math ////////////////////////////////////////////////////////////////////////
export const min = arr => Math.min(...arr)
export const max = arr => Math.max(...arr)
export const sum = arr => arr.reduce((a, b) => a + b)
export const product = arr => arr.reduce((a, b) => a * b)
// Function decorators /////////////////////////////////////////////////////////
export const not = fn => (...args) => !fn(...args)
export const maybe = fn =>
(...args) => {
if (args.length < fn.length || args.some(arg => arg == null)) return
return fn(...args)
}
export const once = fn => {
var done = false
return (...args) => {
if (done) return
done = true
fn(...args)
}
}
export const curry = fn => {
const arity = fn.length;
const curried = (...args) =>
args.length < arity ? (...more) => curried(...args, ...more) : fn(...args)
return curried
}
export const pipeline = (...funcs) =>
value => funcs.reduce((a, b) => b(a), value)
import test from 'ava'
import {
combine,
compact,
difference,
head,
initial,
intersection,
last,
sortedIndex,
tail,
union,
unique,
without,
getValues,
toMap,
min,
max,
sum,
product,
not,
maybe,
once,
curry,
pipeline
} from './functional'
// Array utils /////////////////////////////////////////////////////////////////
test('combine', t =>
t.deepEqual(combine(['foo'], ['bar', 'baz'], [1, 2]), ['foo', 'bar', 'baz', 1, 2])
)
test('compact', t =>
t.deepEqual(compact([0, 1, false, 2, '', 3]), [1, 2, 3])
)
test('difference', t =>
t.deepEqual(difference([1, 2, 3, 4, 5], [5, 2, 10]), [1, 3, 4])
)
test('head', t =>
t.deepEqual(head(['foo', 'bar']), 'foo')
)
test('initial', t =>
t.deepEqual(initial([3, 2, 1]), [3, 2])
)
test('intersection', t =>
t.deepEqual(intersection([1, 2, 3], [101, 2, 1, 10], [2, 1]), [1, 2])
)
test('last', t =>
t.deepEqual(last(['foo', 'bar' ]), 'bar')
)
test('sortedIndex', t =>
t.deepEqual(sortedIndex([10, 20, 30, 40, 50], 35), 3)
)
test('tail', t =>
t.deepEqual(tail(['foo', 'bar', 'baz']), ['bar', 'baz'])
)
test('union', t =>
t.deepEqual(union([1, 2, 3], [101, 2, 1, 10], [2, 1]), [1, 2, 3, 101, 10])
)
test('unique', t =>
t.deepEqual(unique([1, 2, 1, 3, 1, 4]), [1, 2, 3, 4])
)
test('without', t =>
t.deepEqual(without([1, 2, 1, 0, 3, 1, 4], 0, 1), [2, 3, 4])
)
// Object utils ////////////////////////////////////////////////////////////////
test('getValues', t =>
t.deepEqual(getValues({ foo: 'bar', hello: 'world' }), ['bar', 'world'])
)
// ToDo: toMap
// test('toMap', t => {})
// Math ////////////////////////////////////////////////////////////////////////
test('min', t =>
t.deepEqual(min([10, 50, 30]), 10)
)
test('max', t =>
t.deepEqual(max([10, 50, 30]), 50)
)
test('sum', t =>
t.deepEqual(sum([10, 50, 30]), 90)
)
test('product', t =>
t.deepEqual(product([10, 50, 30]), 15000)
)
// Function decorators /////////////////////////////////////////////////////////
test('not', t => {
const isNull = x => x == null
const isSet = not(isNull)
t.deepEqual(isSet(undefined), false)
})
// ToDo: maybe
// test('maybe', t => {})
// ToDo: once
// test('once', t => {})
test('curry', t => {
const add = curry((a, b) => a + b)
t.deepEqual(add(2, 3), 5)
t.deepEqual(add(2)(3), 5)
})
test('pipeline', t => {
const plus1 = a => a + 1
const mult2 = a => a * 2
let addThenMult = pipeline(plus1, mult2)
t.deepEqual(addThenMult(5), 12)
})
@daliborgogic
Copy link
Author

daliborgogic commented Jul 22, 2018

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: 'Dalibor', age: 40 }) // => Map { name: 'Dalibor', age: 40 }

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.

let greet = (message, name) => console.log(message + ' ' + name)
let 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.

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

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