From 13f7975a5525305b0a1baf09ecfa1bd872e94917 Mon Sep 17 00:00:00 2001 From: Coder of Salvation / Leon van Kammen Date: Sat, 2 Jan 2016 11:06:52 +0100 Subject: [PATCH 1/9] Update essential.coffee --- src/essential.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/src/essential.coffee b/src/essential.coffee index df7376f..07c891b 100644 --- a/src/essential.coffee +++ b/src/essential.coffee @@ -42,6 +42,7 @@ nary = λ (n, f) -> (as...) -> f as[0...n]... compose = (fs...) -> fs.reduce (f, g) -> (as...) -> f g as... pcompose = (fs...) -> (xs) -> xs.map (x, i) -> fs[i]? x sequence = nflip compose +psequence = nflip pcompose over = λ (f, g, x, y) -> f g(x), g y From 9c4dfe5fbe420c936f8aa7d9ea1d80f5eb66b896 Mon Sep 17 00:00:00 2001 From: Leon Date: Wed, 6 Jan 2016 10:44:20 +0100 Subject: [PATCH 2/9] added , functions + , aliases + update doc --- README.md | 114 +++++++++++++++++++++++++++++++++---------- lib/essential.js | 66 +++++++++++++++++++++++-- package.json | 1 + src/essential.coffee | 30 ++++++++++-- test/index.coffee | 17 ++++--- 5 files changed, 188 insertions(+), 40 deletions(-) diff --git a/README.md b/README.md index 9508e37..09606e7 100644 --- a/README.md +++ b/README.md @@ -25,29 +25,91 @@ Essential.js is an alternative to [Underscore.js](http://underscorejs.org/) that ## API -```coffeescript -module.exports = { - # Core - _, id, K, - builtin, toArray, - variadic, apply, applyNew, - ncurry, λ, curry, partial, - flip, flip3, nflip, - unary, binary, nary, - compose, pcompose, sequence, over, - notF, not:notF, eq, notEq, typeOf, isType, - toObject, extend, deepExtend, deepClone, forOwn, - fold, fold1, foldr, foldr1, map, filter, any, all, each, indexOf, concat, - slice, first, last, rest, initial, take, drop, - inArray, remove, tails, uniqueBy, unique, dups, - flatten, union, intersection, flatMap, - pluck, deepPluck, where, deepWhere, - values, pairs, interleave, intersperse, intercalate, - zip, zipWith, zipObject, unzipObject, - range, shuffle, - sortBy, groupBy, countBy, - format, template, gmatch, permutations, combinations, powerset, - # Fantasy - fmap, ap, chain, liftA, seqM -} -``` + + + Function Examplecall, Expected output + -------------------------------------- ------------------------------------------------------------------- + + ncurry ncurry(2, add)(1)(2), 3 + curry curry(add)(1)(2), 3 + partial - in order partial(add, 1)(2), 3 + partial - with placeholder partial(add, _, 1)(2), 3 + partial - with placeholder interleaved partial(append, _, 'b', _)('a','c'), 'abc' + apply apply(add, [1,2]), 3 + applyNew applyNew(Person, ['Josh',25]) instanceof Person, true + flip flip(sub)(2, 3), 1 + flip3 flip3((x, y, z) -> x - y - z)(2, 3, 5), 0 + nflip nflip(append)('a','b','c'), 'cba' + unary [1,2,3].map(unary variadic), [[1], [2], [3]] + binary [1,2,3].map(binary variadic), [[1,0], [2,1], [3,2]] + nary [1,2,3].map(nary 1, variadic), [[1], [2], [3]] + compose compose(curry(add)(1), curry(mul)(2))(2), 5 + sequence sequence(curry(add)(1), curry(mul)(2))(2), 6 + seq - shorthand for sequence seq(curry(add)(1), curry(mul)(2))(2), 6 + pipe functions thru eachother pipe(curry(add)(1), curry(mul)(2))(2), 6 + pcompose pcompose(add(1), mul(2), add(3))([1,2,3]), [2,4,6] + over over(add, mul(2), 3, 4), 14 + notF notF(even)(2), false + eq eq('foo')('foo'), true + notEq notEq('foo')('bar'), true + isType [isType('Array',[]), isType('Object',{})], [true, true] + toObject toObject(['a',1,'b',2,'c',3]), {a:1, b:2, c:3} + extend extend(obj, {b:2, c:3}, {c:4}), {a:1, b:2, c:4} + extend - mutates target obj, {a:1, b:2, c:4} + deepExtend - object deepExtend(x, y), {a: {b: 123, c: 2}} + deepClone a, deepClone a + forOwn forOwn([], ((acc, k, v) -> acc.concat [k, v]), {a:1, b:2, c:3}), ['a',1,'b',2,'c',3] + fold fold(0, add, [1,2,3]), 6 + foldr foldr(6, sub, [1,2,3]), 0 + map map(curry(add)(1), [1,2,3]), [2,3,4] + filter filter(even, [1,2,3,4]), [2,4] + any any(even, [1,2,3,4]), true + all all(even, [1,2,3,4]), false + indexOf indexOf(2, [1,2,3]), 1 + concat concat([1,2], [3,4], [5,6]), [1,2,3,4,5,6] + first first([1,2,3]), 1 + last last([1,2,3]), 3 + rest rest([1,2,3]), [2,3] + initial initial([1,2,3]), [1,2] + take take(2, [1,2,3]), [1,2] + drop drop(2, [1,2,3]), [3] + inArray inArray([1,2,3], 2), true + uniqueBy uniqueBy(((x) -> x.length), ['a','b','aa','bb']), ['a','aa'] + unique unique([1,1,2,2,3,3]), [1,2,3] + dups dups([1,1,2,2,3,4]), [1,2] + flatten flatten([1,[2,[3,[4]]]]), [1,2,3,4] + union union([1,2], [2,3], [3,4]), [1,2,3,4] + intersection intersection([1,2,3],[1,2,4],[1,2,5]), [1,2] + flatMap flatMap([1,2], (x) -> flatMap([3,4], (y) -> x + y)), [4,5,5,6] + pluck array pluck(1, [1,2,3]), 2 + pluck object pluck('a', {a:1, b:2, c:3}), 1 + deepPluck true, true + where where({name:'Peter'}, [{name:'Peter'},{name:'Jon'},{name:'Mike'}]), [{name:'Peter'}] + deepWhere deepWhere({a:{b:1}}, [{a:{b:1}}, {c:2}]), [{a:{b:1}}] + values values({a:1, b:2, c:3}), [1,2,3] + pairs pairs({a:1, b:2, c:3}), [['a',1],['b',2],['c',3]] + zip zip([1,2,3], [4,5,6]), [[1,4], [2,5], [3,6]] + zipWith zipWith(add, [1,2,3], [4,5,6]), [5,7,9] + zipObject zipObject(['a','b','c'],[1,2,3]), {a:1, b:2, c:3} + unzipObject unzipObject({a:1, b:2}), [['a','b'], [1,2]] + range range(0,10), [0..10] + shuffle - copies array shuffle(xs) is xs, false + sortBy - collection sortBy(id, [3,4,2,5,1]), [1,2,3,4,5] + groupBy groupBy(Math.round, [1.1,1.2,1.3,1.6,1.7,1.8]), {1:[1.1,1.2,1.3], 2:[1.6,1.7,1.8]} + groupBy - collection groupBy(((x) -> x.n), [{n:1},{n:1},{n:2},{n:2}]), {1:[{n:1},{n:1}],2:[{n:2},{n:2}]} + countBy countBy(Math.round, [1.1,1.2,1.3,1.6,1.7,1.8]), {1:3, 2:3} + format - formatting strings format(['a','c'], '%1b%2d'), 'abcd' + template - string evaluation template({a:'a', c:'c'}, '#{a}b#{c}d'), 'abcd' + gmatch gmatch(/\{(.+?)\}/g, '{a}b{c}d'), ['a','c'] + either - handy for fallback either( ((id) -> null), ((id) -> 'not found'), 1234 ), 'not found' + bindAll - bind obj functions to scope bindAll( obj, [scope] ) + +## Local scope + +If you [don't want to use global scope using expose()](http://stackoverflow.com/questions/2613310/ive-heard-global-variables-are-bad-what-alternative-solution-should-i-use), but +you still want to use FP without `_.` syntax-noise, you can include it in your local scope like this: + + require('essentialjs').local()(require) + diff --git a/lib/essential.js b/lib/essential.js index 0110f11..eec3601 100644 --- a/lib/essential.js +++ b/lib/essential.js @@ -1,4 +1,4 @@ -// Generated by CoffeeScript 1.9.3 +// Generated by CoffeeScript 1.10.0 /* * Essential.js 1.1.20 @@ -7,7 +7,7 @@ */ (function() { - var K, _, all, any, ap, apply, applyNew, binary, builtin, chain, combinations, compose, concat, countBy, curry, deepClone, deepExtend, deepPluck, deepWhere, drop, dups, each, eq, extend, filter, first, flatMap, flatten, flip, flip3, fmap, fold, fold1, foldr, foldr1, forOwn, format, gmatch, groupBy, id, inArray, indexOf, initial, intercalate, interleave, intersection, intersperse, isType, last, liftA, map, nary, ncurry, nflip, notEq, notF, over, pairs, partial, pcompose, permutations, pluck, powerset, range, remove, rest, seqM, sequence, shuffle, slice, sortBy, tails, take, template, toArray, toObject, typeOf, unary, union, unique, uniqueBy, unzipObject, values, variadic, where, zip, zipObject, zipWith, λ, + var K, _, add, all, any, ap, append, apply, applyNew, binary, bindAll, builtin, chain, combinations, compose, concat, countBy, curry, deepClone, deepExtend, deepPluck, deepWhere, drop, dups, each, either, eq, extend, filter, first, flatMap, flatten, flip, flip3, fmap, fold, fold1, foldr, foldr1, forOwn, format, gmatch, groupBy, id, inArray, indexOf, initial, intercalate, interleave, intersection, intersperse, isType, last, liftA, map, mul, nary, ncurry, nflip, notEq, notF, over, pairs, partial, pcompose, permutations, pipe, pluck, powerset, psequence, range, remove, rest, seq, seqM, sequence, shuffle, slice, sortBy, sub, tails, take, template, toArray, toObject, typeOf, unary, union, unique, uniqueBy, unzipObject, values, variadic, where, zip, zipObject, zipWith, λ, slice1 = [].slice, hasProp = {}.hasOwnProperty, indexOf1 = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; @@ -141,7 +141,9 @@ }; }; - sequence = nflip(compose); + pipe = seq = sequence = nflip(compose); + + psequence = nflip(pcompose); over = λ(function(f, g, x, y) { return f(g(x), g(y)); @@ -591,6 +593,39 @@ }, ctor.of([])); }); + add = λ(function(x, y) { + return x + y; + }); + + mul = λ(function(x, y) { + return x * y; + }); + + sub = λ(function(x, y) { + return x - y; + }); + + append = function() { + var as; + as = 1 <= arguments.length ? slice1.call(arguments, 0) : []; + return as.reduce(add); + }; + + either = curry(function(a, b, data) { + return a(data) || b(data); + }); + + bindAll = function(obj, scope) { + if (scope == null) { + scope = obj; + } + return forOwn(obj, function(k, v) { + if (typeof v === "function") { + return obj[k] = v.bind(scope); + } + }); + }; + module.exports = { _: _, id: id, @@ -613,6 +648,8 @@ compose: compose, pcompose: pcompose, sequence: sequence, + seq: seq, + pipe: pipe, over: over, notF: notF, not: notF, @@ -677,6 +714,12 @@ permutations: permutations, combinations: combinations, powerset: powerset, + bindAll: bindAll, + add: add, + mul: mul, + sub: sub, + append: append, + either: either, fmap: fmap, ap: ap, chain: chain, @@ -686,4 +729,21 @@ module.exports.expose = partial(extend, _, module.exports); + module.exports.local = (function() { + var code; + code = function() { + var k, str, v; + str = 'var ejs = require("essentialjs")'; + for (k in this) { + v = this[k]; + if (k !== '_') { + str += " ;" + k + " = ejs." + k; + } + } + return str + ";"; + }; + code.apply(this); + return new Function('require', code.apply(this)); + }).bind(module.exports); + }).call(this); diff --git a/package.json b/package.json index 98d5f1a..4a35afa 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "scripts": { "compile": "coffee -c -o lib src/essential.coffee ", "test": "coffee test", + "gendoc": "grep '^test ' test/index.coffee | sed \"s/^test //g;s/^'//g;s/',/@/\" | column -t -s'@' | awk '{print \" \"$0}'", "prepublish": "npm test && npm run-script compile" }, "licenses": [{ diff --git a/src/essential.coffee b/src/essential.coffee index 07c891b..3cb9dc4 100644 --- a/src/essential.coffee +++ b/src/essential.coffee @@ -41,7 +41,7 @@ nary = λ (n, f) -> (as...) -> f as[0...n]... compose = (fs...) -> fs.reduce (f, g) -> (as...) -> f g as... pcompose = (fs...) -> (xs) -> xs.map (x, i) -> fs[i]? x -sequence = nflip compose +pipe = seq = sequence = nflip compose psequence = nflip pcompose over = λ (f, g, x, y) -> f g(x), g y @@ -278,6 +278,20 @@ seqM = λ (ctor, ms) -> ctor.of [] ) +add = λ (x, y) -> x + y +mul = λ (x, y) -> x * y +sub = λ (x, y) -> x - y +append = (as...) -> as.reduce add + +# either provides an easy fallback for default data (`either foo(),default()`) +either = curry (a,b,data) -> a(data) || b(data) + +# easily map all functions of a module to itself (or another scope) +# to ensure `this` reference from changing +bindAll = (obj,scope) -> + scope = obj if not scope? + forOwn obj, (k,v) -> obj[k] = v.bind scope if typeof v is "function" + # Exports # module.exports = { @@ -288,7 +302,7 @@ module.exports = { ncurry, λ, curry, partial, flip, flip3, nflip, unary, binary, nary, - compose, pcompose, sequence, over, + compose, pcompose, sequence, seq, pipe, over, notF, not:notF, eq, notEq, typeOf, isType, toObject, extend, deepExtend, deepClone, forOwn, fold, fold1, foldr, foldr1, map, filter, any, all, each, indexOf, concat, @@ -300,9 +314,19 @@ module.exports = { zip, zipWith, zipObject, unzipObject, range, shuffle, sortBy, groupBy, countBy, - format, template, gmatch, permutations, combinations, powerset, + format, template, gmatch, permutations, combinations, powerset, bindAll + # Math / Logical + add, mul, sub, append, either, # Fantasy fmap, ap, chain, liftA, seqM } module.exports.expose = partial extend, _, module.exports +module.exports.local = ( () -> + code = () -> + str = 'var ejs = require("essentialjs")' + str += " ;#{k} = ejs.#{k}" for k,v of @ when k != '_' + return str+";" + code.apply @ + new Function( 'require', code.apply @ ) +).bind module.exports diff --git a/test/index.coffee b/test/index.coffee index 6a7d02b..8dcb0cd 100644 --- a/test/index.coffee +++ b/test/index.coffee @@ -5,11 +5,6 @@ test = (name, result, expected) -> assert result, expected, "#{name}: expected #{JSON.stringify expected} but got #{JSON.stringify result}" console.log "#{name} ✓" -add = λ (x, y) -> x + y -mul = λ (x, y) -> x * y -sub = λ (x, y) -> x - y -append = (as...) -> as.reduce add - even = (x) -> x % 2 is 0 class Person @@ -37,6 +32,8 @@ test 'nary', [1,2,3].map(nary 1, variadic), [[1], [2], [3]] test 'compose', compose(curry(add)(1), curry(mul)(2))(2), 5 test 'sequence', sequence(curry(add)(1), curry(mul)(2))(2), 6 +test 'seq - shorthand for sequence', seq(curry(add)(1), curry(mul)(2))(2), 6 +test 'pipe functions thru eachother', pipe(curry(add)(1), curry(mul)(2))(2), 6 test 'pcompose', pcompose(add(1), mul(2), add(3))([1,2,3]), [2,4,6] test 'over', over(add, mul(2), 3, 4), 14 @@ -109,15 +106,19 @@ test 'range', range(0,10), [0..10] xs = [1..5] test 'shuffle - copies array', shuffle(xs) is xs, false -test 'sortBy', sortBy(id, [3,4,2,5,1]), [1,2,3,4,5] +test 'sortBy - collection', sortBy(id, [3,4,2,5,1]), [1,2,3,4,5] test 'groupBy', groupBy(Math.round, [1.1,1.2,1.3,1.6,1.7,1.8]), {1:[1.1,1.2,1.3], 2:[1.6,1.7,1.8]} test 'groupBy - collection', groupBy(((x) -> x.n), [{n:1},{n:1},{n:2},{n:2}]), {1:[{n:1},{n:1}],2:[{n:2},{n:2}]} test 'countBy', countBy(Math.round, [1.1,1.2,1.3,1.6,1.7,1.8]), {1:3, 2:3} -test 'format', format(['a','c'], '%1b%2d'), 'abcd' -test 'template', template({a:'a', c:'c'}, '#{a}b#{c}d'), 'abcd' +test 'format - formatting strings', format(['a','c'], '%1b%2d'), 'abcd' +test 'template - string evaluation', template({a:'a', c:'c'}, '#{a}b#{c}d'), 'abcd' test 'gmatch', gmatch(/\{(.+?)\}/g, '{a}b{c}d'), ['a','c'] +test 'either - handy for fallback', either( ((id) -> null), ((id) -> 'not found'), 1234 ), 'not found' + +test 'bindAll - bind obj functions to scope', true, true + ### TODO remove tails From 2f6525ac604cc03e624c58581124f1720a4efc36 Mon Sep 17 00:00:00 2001 From: Coder of Salvation Date: Fri, 8 Jan 2016 14:18:51 +0100 Subject: [PATCH 3/9] added support for object in map() and filter() as shown in by Brian Lonsdorf's talk 'Hey Underscore, You're Doing it Wrong' --- package.json | 2 +- src/essential.coffee | 17 +++++++++++++++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 4a35afa..468f7da 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "author": "elclanrs", "name": "essentialjs", "description": "Essential helpers for functional JavaScript", - "version": "1.1.20", + "version": "1.1.21", "main": "lib/essential.js", "repository": { "type": "git", diff --git a/src/essential.coffee b/src/essential.coffee index 3cb9dc4..d4d9f8f 100644 --- a/src/essential.coffee +++ b/src/essential.coffee @@ -91,8 +91,21 @@ fold = flip3 builtin Array::reduce fold1 = λ (f, xs) -> fold xs[0], f, xs foldr = flip3 builtin Array::reduceRight foldr1 = λ (f, xs) -> foldr xs[0], f, xs -map = flip builtin Array::map -filter = flip builtin Array::filter +map = ncurry( 2, () -> + if isType 'Object', arguments[1] + result = {} ; f = arguments[0] + result[k] = f(v) for k,v of obj + return result + else return arguments[1].map arguments[0] +) +filter = ncurry( 2, () -> + if isType 'Object', arguments[1] + result = {} ; f = arguments[0] + result[k] = v for k,v of obj when f(v,k) + result + else return arguments[1].filter arguments[0] +) + any = flip builtin Array::some all = flip builtin Array::every each = flip builtin Array::forEach From 3a808be5bb48fd43e9040d72aacf64181595c9e0 Mon Sep 17 00:00:00 2001 From: Coder of Salvation Date: Fri, 8 Jan 2016 14:20:13 +0100 Subject: [PATCH 4/9] compiled js --- lib/essential.js | 34 +++++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/lib/essential.js b/lib/essential.js index eec3601..4abdcde 100644 --- a/lib/essential.js +++ b/lib/essential.js @@ -1,4 +1,4 @@ -// Generated by CoffeeScript 1.10.0 +// Generated by CoffeeScript 1.9.3 /* * Essential.js 1.1.20 @@ -241,9 +241,37 @@ return foldr(xs[0], f, xs); }); - map = flip(builtin(Array.prototype.map)); + map = ncurry(2, function() { + var f, k, result, v; + if (isType('Object', arguments[1])) { + result = {}; + f = arguments[0]; + for (k in obj) { + v = obj[k]; + result[k] = f(v); + } + return result; + } else { + return arguments[1].map(arguments[0]); + } + }); - filter = flip(builtin(Array.prototype.filter)); + filter = ncurry(2, function() { + var f, k, result, v; + if (isType('Object', arguments[1])) { + result = {}; + f = arguments[0]; + for (k in obj) { + v = obj[k]; + if (f(v, k)) { + result[k] = v; + } + } + return result; + } else { + return arguments[1].filter(arguments[0]); + } + }); any = flip(builtin(Array.prototype.some)); From 45aec93ba14cd6bd5f231084211a731b747b8fe5 Mon Sep 17 00:00:00 2001 From: Coder of Salvation Date: Fri, 8 Jan 2016 17:35:21 +0100 Subject: [PATCH 5/9] update --- lib/essential.js | 43 ++++++++++++++++--------------------------- package.json | 2 +- src/essential.coffee | 22 +++++++++++----------- test/index.coffee | 6 ++++-- 4 files changed, 32 insertions(+), 41 deletions(-) diff --git a/lib/essential.js b/lib/essential.js index 4abdcde..03c1065 100644 --- a/lib/essential.js +++ b/lib/essential.js @@ -7,7 +7,7 @@ */ (function() { - var K, _, add, all, any, ap, append, apply, applyNew, binary, bindAll, builtin, chain, combinations, compose, concat, countBy, curry, deepClone, deepExtend, deepPluck, deepWhere, drop, dups, each, either, eq, extend, filter, first, flatMap, flatten, flip, flip3, fmap, fold, fold1, foldr, foldr1, forOwn, format, gmatch, groupBy, id, inArray, indexOf, initial, intercalate, interleave, intersection, intersperse, isType, last, liftA, map, mul, nary, ncurry, nflip, notEq, notF, over, pairs, partial, pcompose, permutations, pipe, pluck, powerset, psequence, range, remove, rest, seq, seqM, sequence, shuffle, slice, sortBy, sub, tails, take, template, toArray, toObject, typeOf, unary, union, unique, uniqueBy, unzipObject, values, variadic, where, zip, zipObject, zipWith, λ, + var K, _, add, all, any, ap, append, apply, applyNew, binary, bindAll, builtin, chain, combinations, compose, concat, countBy, curry, deepClone, deepExtend, deepPluck, deepWhere, drop, dups, each, either, eq, extend, filter, first, flatMap, flatten, flip, flip3, fmap, fold, fold1, foldr, foldr1, forOwn, format, gmatch, groupBy, id, inArray, indexOf, initial, intercalate, interleave, intersection, intersperse, isType, last, liftA, map, mapKeys, mul, nary, ncurry, nflip, notEq, notF, over, pairs, partial, pcompose, permutations, pipe, pluck, powerset, psequence, range, remove, rest, seq, seqM, sequence, shuffle, slice, sortBy, sub, tails, take, template, toArray, toObject, typeOf, unary, union, unique, uniqueBy, unzipObject, values, variadic, where, zip, zipObject, zipWith, λ, slice1 = [].slice, hasProp = {}.hasOwnProperty, indexOf1 = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; @@ -242,35 +242,23 @@ }); map = ncurry(2, function() { - var f, k, result, v; - if (isType('Object', arguments[1])) { - result = {}; - f = arguments[0]; - for (k in obj) { - v = obj[k]; - result[k] = f(v); - } - return result; - } else { - return arguments[1].map(arguments[0]); - } + var input, obj, result; + obj = false; + input = (isType('Object', arguments[1]) ? (obj = unzipObject(arguments[1]))[1] : arguments[1]); + result = input.map(arguments[0]); + return (obj ? zipObject(obj[0], result) : result); }); + mapKeys = function(obj, f) { + return zipObject(map(arguments[0], Object.keys(arguments[1])), unzipObject(arguments[1])[1]); + }; + filter = ncurry(2, function() { - var f, k, result, v; - if (isType('Object', arguments[1])) { - result = {}; - f = arguments[0]; - for (k in obj) { - v = obj[k]; - if (f(v, k)) { - result[k] = v; - } - } - return result; - } else { - return arguments[1].filter(arguments[0]); - } + var input, obj, result; + obj = false; + input = (isType('Object', arguments[1]) ? (obj = unzipObject(arguments[1]))[1] : arguments[1]); + result = input.filter(arguments[0]); + return (obj ? zipObject(obj[0], result) : result); }); any = flip(builtin(Array.prototype.some)); @@ -695,6 +683,7 @@ foldr: foldr, foldr1: foldr1, map: map, + mapKeys: mapKeys, filter: filter, any: any, all: all, diff --git a/package.json b/package.json index 468f7da..6fa6348 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "author": "elclanrs", "name": "essentialjs", "description": "Essential helpers for functional JavaScript", - "version": "1.1.21", + "version": "1.1.22", "main": "lib/essential.js", "repository": { "type": "git", diff --git a/src/essential.coffee b/src/essential.coffee index d4d9f8f..294d027 100644 --- a/src/essential.coffee +++ b/src/essential.coffee @@ -92,18 +92,18 @@ fold1 = λ (f, xs) -> fold xs[0], f, xs foldr = flip3 builtin Array::reduceRight foldr1 = λ (f, xs) -> foldr xs[0], f, xs map = ncurry( 2, () -> - if isType 'Object', arguments[1] - result = {} ; f = arguments[0] - result[k] = f(v) for k,v of obj - return result - else return arguments[1].map arguments[0] + obj = false + input = ( if isType 'Object', arguments[1] then (obj = unzipObject(arguments[1]))[1] else arguments[1] ) + result = input.map arguments[0] + return ( if obj then zipObject obj[0], result else result ) ) +mapKeys = (obj,f) -> zipObject map( arguments[0], Object.keys arguments[1]), unzipObject(arguments[1])[1] + filter = ncurry( 2, () -> - if isType 'Object', arguments[1] - result = {} ; f = arguments[0] - result[k] = v for k,v of obj when f(v,k) - result - else return arguments[1].filter arguments[0] + obj = false + input = ( if isType 'Object', arguments[1] then (obj = unzipObject(arguments[1]))[1] else arguments[1] ) + result = input.filter arguments[0] + return ( if obj then zipObject obj[0], result else result ) ) any = flip builtin Array::some @@ -318,7 +318,7 @@ module.exports = { compose, pcompose, sequence, seq, pipe, over, notF, not:notF, eq, notEq, typeOf, isType, toObject, extend, deepExtend, deepClone, forOwn, - fold, fold1, foldr, foldr1, map, filter, any, all, each, indexOf, concat, + fold, fold1, foldr, foldr1, map, mapKeys, filter, any, all, each, indexOf, concat, slice, first, last, rest, initial, take, drop, inArray, remove, tails, uniqueBy, unique, dups, flatten, union, intersection, flatMap, diff --git a/test/index.coffee b/test/index.coffee index 8dcb0cd..deee6ee 100644 --- a/test/index.coffee +++ b/test/index.coffee @@ -2,8 +2,8 @@ require('../src/essential.coffee').expose global assert = require('assert').deepEqual test = (name, result, expected) -> - assert result, expected, "#{name}: expected #{JSON.stringify expected} but got #{JSON.stringify result}" - console.log "#{name} ✓" + assert JSON.stringify(result), JSON.stringify(expected), "#{name}: expected #{JSON.stringify expected} but got #{JSON.stringify result}" + console.log "#{name}✓" even = (x) -> x % 2 is 0 @@ -61,6 +61,8 @@ test 'forOwn', forOwn([], ((acc, k, v) -> acc.concat [k, v]), {a:1, b:2, c:3}), test 'fold', fold(0, add, [1,2,3]), 6 test 'foldr', foldr(6, sub, [1,2,3]), 0 test 'map', map(curry(add)(1), [1,2,3]), [2,3,4] +test 'map object values', map( curry(add)(1), {id:1,foo:1}), {id:2,foo:2} +test 'map object keys', mapKeys( curry(add)(1), {id:1,foo:1}), {'1id':1,'1foo':1} test 'filter', filter(even, [1,2,3,4]), [2,4] test 'any', any(even, [1,2,3,4]), true test 'all', all(even, [1,2,3,4]), false From 5acfb5ed8d1742111aaaf8feaae9b8a8ab033bdf Mon Sep 17 00:00:00 2001 From: Leon Date: Fri, 22 Jan 2016 22:26:59 +0100 Subject: [PATCH 6/9] add asyncMap --- README.md | 14 ++++++++++++++ lib/essential.js | 27 +++++++++++++++++++++++++-- src/essential.coffee | 15 ++++++++++++++- test/index.coffee | 9 +++++++++ 4 files changed, 62 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 09606e7..a1355c4 100644 --- a/README.md +++ b/README.md @@ -106,6 +106,20 @@ generated with: `npm run-script gendoc` either - handy for fallback either( ((id) -> null), ((id) -> 'not found'), 1234 ), 'not found' bindAll - bind obj functions to scope bindAll( obj, [scope] ) + + # async map + var arr, done; + arr = [400, 300, 200, 100]; + done = function(err) { + console.log("async done"); + if( err ) console.log( err.toString() ); + }; + mapAsync(arr, done, function(v, k, next) { + return setTimeout(function() { + return next(); + }, v); + }); + ## Local scope If you [don't want to use global scope using expose()](http://stackoverflow.com/questions/2613310/ive-heard-global-variables-are-bad-what-alternative-solution-should-i-use), but diff --git a/lib/essential.js b/lib/essential.js index 03c1065..8f729d5 100644 --- a/lib/essential.js +++ b/lib/essential.js @@ -1,4 +1,4 @@ -// Generated by CoffeeScript 1.9.3 +// Generated by CoffeeScript 1.10.0 /* * Essential.js 1.1.20 @@ -7,7 +7,7 @@ */ (function() { - var K, _, add, all, any, ap, append, apply, applyNew, binary, bindAll, builtin, chain, combinations, compose, concat, countBy, curry, deepClone, deepExtend, deepPluck, deepWhere, drop, dups, each, either, eq, extend, filter, first, flatMap, flatten, flip, flip3, fmap, fold, fold1, foldr, foldr1, forOwn, format, gmatch, groupBy, id, inArray, indexOf, initial, intercalate, interleave, intersection, intersperse, isType, last, liftA, map, mapKeys, mul, nary, ncurry, nflip, notEq, notF, over, pairs, partial, pcompose, permutations, pipe, pluck, powerset, psequence, range, remove, rest, seq, seqM, sequence, shuffle, slice, sortBy, sub, tails, take, template, toArray, toObject, typeOf, unary, union, unique, uniqueBy, unzipObject, values, variadic, where, zip, zipObject, zipWith, λ, + var K, _, add, all, any, ap, append, apply, applyNew, binary, bindAll, builtin, chain, combinations, compose, concat, countBy, curry, deepClone, deepExtend, deepPluck, deepWhere, drop, dups, each, either, eq, extend, filter, first, flatMap, flatten, flip, flip3, fmap, fold, fold1, foldr, foldr1, forOwn, format, gmatch, groupBy, id, inArray, indexOf, initial, intercalate, interleave, intersection, intersperse, isType, last, liftA, map, mapAsync, mapKeys, mul, nary, ncurry, nflip, notEq, notF, over, pairs, partial, pcompose, permutations, pipe, pluck, powerset, psequence, range, remove, rest, seq, seqM, sequence, shuffle, slice, sortBy, sub, tails, take, template, toArray, toObject, typeOf, unary, union, unique, uniqueBy, unzipObject, values, variadic, where, zip, zipObject, zipWith, λ, slice1 = [].slice, hasProp = {}.hasOwnProperty, indexOf1 = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; @@ -642,12 +642,35 @@ }); }; + mapAsync = function(arr, done, cb) { + var f, funcs, i, k, v; + funcs = []; + i = 0; + for (k in arr) { + v = arr[k]; + f = function(i, v) { + return function() { + var e, error; + try { + return cb(v, i, funcs[i + 1] || done); + } catch (error) { + e = error; + return done(new Error(e)); + } + }; + }; + funcs.push(f(i++, v)); + } + return funcs[0](); + }; + module.exports = { _: _, id: id, K: K, builtin: builtin, toArray: toArray, + mapAsync: mapAsync, variadic: variadic, apply: apply, applyNew: applyNew, diff --git a/src/essential.coffee b/src/essential.coffee index 294d027..8c98198 100644 --- a/src/essential.coffee +++ b/src/essential.coffee @@ -304,13 +304,26 @@ either = curry (a,b,data) -> a(data) || b(data) bindAll = (obj,scope) -> scope = obj if not scope? forOwn obj, (k,v) -> obj[k] = v.bind scope if typeof v is "function" + +mapAsync = (arr,done,cb) -> + funcs = [] ; i=0 + for k,v of arr + f = (i,v) -> + () -> + try + cb v,i, funcs[i+1] || done + catch e + done new Error(e) + funcs.push f(i++,v) + funcs[0]() + # Exports # module.exports = { # Core _, id, K, - builtin, toArray, + builtin, toArray, mapAsync, variadic, apply, applyNew, ncurry, λ, curry, partial, flip, flip3, nflip, diff --git a/test/index.coffee b/test/index.coffee index deee6ee..911cafc 100644 --- a/test/index.coffee +++ b/test/index.coffee @@ -121,6 +121,15 @@ test 'either - handy for fallback', either( ((id) -> null), ((id) -> 'not found' test 'bindAll - bind obj functions to scope', true, true + +# async map over array +arr = [400,300,200,100] +done = (err) -> console.log "async done" +mapAsync arr, done, (v,k,next) -> + setTimeout () -> + next() + ,v + ### TODO remove tails From 15bac4406a72190f63a0610242fae035cba7939c Mon Sep 17 00:00:00 2001 From: Leon Date: Sun, 10 Jul 2016 00:30:00 +0200 Subject: [PATCH 7/9] minor bugfix --- README.md | 7 +++++++ lib/essential.js | 31 +++++++++++++++++++++++++++++-- package.json | 4 ++-- src/essential.coffee | 14 ++++++++++++-- 4 files changed, 50 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index a1355c4..15c6b6e 100644 --- a/README.md +++ b/README.md @@ -120,6 +120,13 @@ generated with: `npm run-script gendoc` }, v); }); + # reactive streams + onbuttonpress = pipe( + createEventStream("#mybutton",'click'), + pick('target'), + pick('value') + ) + ## Local scope If you [don't want to use global scope using expose()](http://stackoverflow.com/questions/2613310/ive-heard-global-variables-are-bad-what-alternative-solution-should-i-use), but diff --git a/lib/essential.js b/lib/essential.js index 8f729d5..db6ffd7 100644 --- a/lib/essential.js +++ b/lib/essential.js @@ -7,7 +7,7 @@ */ (function() { - var K, _, add, all, any, ap, append, apply, applyNew, binary, bindAll, builtin, chain, combinations, compose, concat, countBy, curry, deepClone, deepExtend, deepPluck, deepWhere, drop, dups, each, either, eq, extend, filter, first, flatMap, flatten, flip, flip3, fmap, fold, fold1, foldr, foldr1, forOwn, format, gmatch, groupBy, id, inArray, indexOf, initial, intercalate, interleave, intersection, intersperse, isType, last, liftA, map, mapAsync, mapKeys, mul, nary, ncurry, nflip, notEq, notF, over, pairs, partial, pcompose, permutations, pipe, pluck, powerset, psequence, range, remove, rest, seq, seqM, sequence, shuffle, slice, sortBy, sub, tails, take, template, toArray, toObject, typeOf, unary, union, unique, uniqueBy, unzipObject, values, variadic, where, zip, zipObject, zipWith, λ, + var K, _, add, all, any, ap, append, apply, applyNew, binary, bindAll, builtin, chain, combinations, compose, concat, countBy, createEventStream, curry, deepClone, deepExtend, deepPluck, deepWhere, drop, dups, each, either, eq, extend, filter, first, flatMap, flatten, flip, flip3, fmap, fold, fold1, foldr, foldr1, forOwn, format, gmatch, groupBy, id, inArray, indexOf, initial, intercalate, interleave, intersection, intersperse, isType, last, liftA, map, mapAsync, mapKeys, mul, nary, ncurry, nflip, notEq, notF, over, pairs, partial, pcompose, permutations, pipe, pluck, powerset, psequence, range, remove, rest, seq, seqM, sequence, shuffle, slice, sortBy, sub, tails, take, template, toArray, toObject, typeOf, unary, union, unique, uniqueBy, unzipObject, values, variadic, where, zip, zipObject, zipWith, λ, slice1 = [].slice, hasProp = {}.hasOwnProperty, indexOf1 = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; @@ -649,10 +649,15 @@ for (k in arr) { v = arr[k]; f = function(i, v) { + console.log("i="+i) return function() { var e, error; try { - return cb(v, i, funcs[i + 1] || done); + if (funcs[i + 1] != null) { + return cb(v, i, funcs[i + 1]); + } else { + return cb(v, i, done ) + } } catch (error) { e = error; return done(new Error(e)); @@ -661,9 +666,30 @@ }; funcs.push(f(i++, v)); } + console.log("funcs="+funcs.length) return funcs[0](); }; + createEventStream = function(selector, event) { + return function(next) { + var element, elements, l, len, results; + if (selector[0] === "#") { + element = document.querySelector(selector); + if (element) { + return element.addEventListener(event, next); + } + } else { + elements = document.querySelectorAll(selector); + results = []; + for (l = 0, len = elements.length; l < len; l++) { + element = elements[l]; + results.push(element.addEventListener(event, next)); + } + return results; + } + }; + }; + module.exports = { _: _, id: id, @@ -671,6 +697,7 @@ builtin: builtin, toArray: toArray, mapAsync: mapAsync, + createEventStream: createEventStream, variadic: variadic, apply: apply, applyNew: applyNew, diff --git a/package.json b/package.json index 6fa6348..1193932 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "author": "elclanrs", "name": "essentialjs", "description": "Essential helpers for functional JavaScript", - "version": "1.1.22", + "version": "1.1.3", "main": "lib/essential.js", "repository": { "type": "git", @@ -12,7 +12,7 @@ "coffeeify": "^0.7.0" }, "scripts": { - "compile": "coffee -c -o lib src/essential.coffee ", + "compile": "coffee -c -o lib src/essential.coffee ; browserify --outfile essential.min.js -r .", "test": "coffee test", "gendoc": "grep '^test ' test/index.coffee | sed \"s/^test //g;s/^'//g;s/',/@/\" | column -t -s'@' | awk '{print \" \"$0}'", "prepublish": "npm test && npm run-script compile" diff --git a/src/essential.coffee b/src/essential.coffee index 8c98198..f604adc 100644 --- a/src/essential.coffee +++ b/src/essential.coffee @@ -311,19 +311,29 @@ mapAsync = (arr,done,cb) -> f = (i,v) -> () -> try - cb v,i, funcs[i+1] || done + if funcs[i+1]? + cb v,i, funcs[i+1] + else cb v,i, done catch e done new Error(e) funcs.push f(i++,v) funcs[0]() +createEventStream = (selector,event) -> + (next) -> + if selector[0] == "#" + element = document.querySelector(selector) + element.addEventListener( event, next ) if element + else + elements = document.querySelectorAll(selector) + element.addEventListener( event, next ) for element in elements # Exports # module.exports = { # Core _, id, K, - builtin, toArray, mapAsync, + builtin, toArray, mapAsync, createEventStream, variadic, apply, applyNew, ncurry, λ, curry, partial, flip, flip3, nflip, From 274c5652f05d8f9bcc5996f5da8d9fcd2c43b88c Mon Sep 17 00:00:00 2001 From: Leon Date: Sat, 1 Oct 2016 13:49:17 +0200 Subject: [PATCH 8/9] updated mapAsync --- lib/essential.js | 5 ++--- src/essential.coffee | 1 + 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/essential.js b/lib/essential.js index db6ffd7..0db4610 100644 --- a/lib/essential.js +++ b/lib/essential.js @@ -643,20 +643,20 @@ }; mapAsync = function(arr, done, cb) { + if( !arr || arr.length == 0 ) done() var f, funcs, i, k, v; funcs = []; i = 0; for (k in arr) { v = arr[k]; f = function(i, v) { - console.log("i="+i) return function() { var e, error; try { if (funcs[i + 1] != null) { return cb(v, i, funcs[i + 1]); } else { - return cb(v, i, done ) + return cb(v, i, done); } } catch (error) { e = error; @@ -666,7 +666,6 @@ }; funcs.push(f(i++, v)); } - console.log("funcs="+funcs.length) return funcs[0](); }; diff --git a/src/essential.coffee b/src/essential.coffee index f604adc..78a8a25 100644 --- a/src/essential.coffee +++ b/src/essential.coffee @@ -306,6 +306,7 @@ bindAll = (obj,scope) -> forOwn obj, (k,v) -> obj[k] = v.bind scope if typeof v is "function" mapAsync = (arr,done,cb) -> + done() if not arr or arr.length == 0 funcs = [] ; i=0 for k,v of arr f = (i,v) -> From f4572cd0b29614e510a512be121294dd6841ead3 Mon Sep 17 00:00:00 2001 From: Leon Date: Thu, 28 May 2020 20:49:24 +0200 Subject: [PATCH 9/9] added sponsor-button --- .github/FUNDING.yml | 1 + 1 file changed, 1 insertion(+) create mode 100644 .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..f744573 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +custom: https://gumroad.com/l/hGYGh