Skip to content

Instantly share code, notes, and snippets.

@elclanrs
Last active July 14, 2016 11:36
Show Gist options
  • Save elclanrs/7ed94150d0052c6ab061 to your computer and use it in GitHub Desktop.
Save elclanrs/7ed94150d0052c6ab061 to your computer and use it in GitHub Desktop.

Install

npm install funcoffee

Require

require('funcoffee').expose global
###!
* funcoffee
* Essential functional programming helpers
* @author elclanrs
* @license MIT
###
#- Essential
_ = {}
nop = ->
id = (x) -> x
builtin = (f) -> nop.call.bind f
variadic = (as...) -> as
apply = (f, as...) -> f [].concat.apply([], as)...
notF = (f) -> (as...) -> not f as...
curry = (f) -> do (recur = (as) ->
next = (bs...) ->
args = (as or [])[0..]
if args.push.apply(args, bs) < f.length and bs.length
return recur args
f args...
if f.length > 1 then next else f
)
partial = (f, as...) -> (bs...) ->
args = as.concat bs
i = args.length
while i-- then if args[i] is _
args[i] = args.splice(-1)[0]
f args...
flip = curry (f, x, y) -> f [y, x]...
flip3 = curry (f, x, y, z) -> f [z, y, x]...
flipN = (f) -> (as...) -> f as.reverse()...
compose = (fs...) -> fs.reduce (f, g) -> (as...) -> f g as...
sequence = flipN compose
#- Types
isType = curry (t, x) -> Object::toString.call(x).slice(8,-1) is t
instanceOf = curry (ctor, x) -> x instanceof ctor
#- Logic
isF = curry (x, y) -> x is y
isntF = notF isF
andF = curry (x, y) -> y && x
orF = curry (x, y) -> y || x
gte = curry (x, y) -> y >= x
lte = curry (x, y) -> y <= x
gt = notF lte
lt = notF gte
#- Math
mod = curry (x, y) -> y % x is 0
even = mod 2
odd = notF even
add = curry (x, y) -> x + y
sub = curry (x, y) -> y - x
mul = curry (x, y) -> x * y
div = curry (x, y) -> y / x
min = partial apply, Math.min
max = partial apply, Math.max
#- Arrays
toArray = builtin Array::slice
find = flip builtin Array::indexOf
each = flip builtin Array::forEach
map = flip builtin Array::map
filter = flip builtin Array::filter
fold = flip3 builtin Array::reduce
foldr = flip3 builtin Array::reduceRight
all = flip builtin Array::every
any = flip builtin Array::some
reverse = builtin Array::reverse
join = flip builtin Array::join
concat = builtin Array::concat
sort = flip builtin Array::sort
inArray = curry (x, xs) -> x in xs
slice = curry (i, j, xs) -> if j? then xs[i...j] else xs[i..]
first = ([x, xs...]) -> x
last = ([xs..., x]) -> x
rest = slice 1, null
initial = slice 0, -1
take = slice 0
drop = partial slice, _, null, _
reject = curry (f, xs) -> filter notF(f), xs
compact = filter Boolean
unique = (xs) -> xs.filter (x, i) -> xs.indexOf(x) is i
dups = (xs) -> xs.filter (x, i) -> xs.indexOf(x) isnt i
zip = (xss...) -> xss[0].map (_, i) -> xss.map (xs) -> xs[i]
zipWith = (f, xs...) -> map partial(apply, f), apply zip, xs
flatten = (xs) ->
xs.reduce (acc, x) ->
if [].concat(x).some Array.isArray
return acc.concat flatten x
acc.concat x
,[]
flatMap = flip compose flatten, map
union = compose unique, flatten, variadic
intersection = compose unique, dups, flatten, variadic
partition = curry (f, xs) -> [filter(f, xs), reject f, xs]
difference = (xs, as...) -> reject inArray(unique(flatten as)), xs
concatF = (fs...) -> (x) ->
fs.reduce (acc, f, i) ->
acc[i] = f x; acc
,[]
shuffle = (xs) ->
for i in [xs.length-1..1]
j = Math.random() * (i + 1) |0
[xs[i], xs[j]] = [xs[j], xs[i]]
xs
#- Strings
split = flip builtin String::split
match = flip builtin String::match
replace = flip3 builtin String::replace
search = flip builtin String::search
substr = flip3 builtin String::substr
trim = builtin String::trim
toUpper = builtin String::toUpperCase
toLower = builtin String::toLowerCase
words = split ' '
unwords = join ' '
capitalize = replace toUpper, /^./
gmatch = curry (re, x) ->
res = []
x.replace re, (as...) ->
res.push.apply res, as[1...-2]
res
#- Objects
toObject = (xs) ->
xs.reduce (acc, x, i) ->
acc[xs[i-1]] = x if i % 2 isnt 0
acc
,{}
forOwn = curry (acc, f, obj) ->
i = 0
for own k, v of obj
acc = f [acc, k, v, i++]...
acc
pluck = curry (x, xs) ->
String(x).split('.').reduce (acc, x) ->
if x of Object acc then acc[x] else undefined
,xs
pluckR = curry (x, xs) -> (xs while xs = pluck x, xs)
pluckF = (x, as...) -> (obj) -> obj[x] as...
pairs = forOwn [], (acc, k, v) -> acc.push [k, v]; acc
zipObject = compose toObject, flatten, zip
unzipObject = forOwn [[],[]], (acc, k, v, i) ->
acc[0][i] = k; acc[1][i] = v
acc
keys = Object.keys
values = compose pluck(1), unzipObject
size = compose pluck('length'), keys
has = compose notF(isType('Undefined')), pluck
extend = partial forOwn, _, ((x, k, v) -> x[k] = v; x), _
merge = curry (x, y) -> extend extend({}, x), y
#- Collections
where = curry (obj, xs) ->
xs.filter (x) ->
Object.keys(obj).every (k) ->
obj[k] is x[k]
sortBy = curry (f, xs) ->
xs.sort withF f, (x, y) ->
if typeof x is 'number' then return x - y
else if x > y then return 1
else if x < y then return -1
else 0
groupBy = curry (f, xs) ->
xs.reduce (acc, x) ->
fx = f x
acc[fx] = (acc[fx] or []).concat x
acc
,{}
countBy = sequence groupBy, forOwn (acc, k, v) ->
acc[k] = v.length
acc
#- Functions
withF = curry (f, g) -> compose partial(apply, g), map(f), variadic
memo = (f) ->
store = {}
(x) ->
return store[x] if x of store
store[x] = f x
#- Export
module.exports = {
_, nop, id, builtin, variadic, apply, notF,
curry, partial, flip, flip3, flipN, compose, sequence,
isType, instanceOf, isF, isntF, andF, orF, gt, lt, gte, lte,
mod, even, odd, add, sub, mul, div, min, max,
toArray, find, each, map, filter, fold, foldr, all, any,
reverse, join, concat, sort,
inArray, slice, first, last, rest, initial, take, drop,
reject, compact, unique, dups, zip, zipWith, flatten,
flatMap, union, intersection, partition, difference, concatF, shuffle,
split, match, replace, search, substr, trim,
toUpper, toLower, words, unwords, capitalize, gmatch,
toObject, forOwn, pluck, pluckR, pluckF,
pairs, zipObject, unzipObject, keys, values, size, has,
extend, merge,
where, sortBy, groupBy, countBy,
withF, memo
}
module.exports.expose = partial extend, _, module.exports
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment