Javascript – functional programming and lambdas
- Processes, standards and quality
- Technologies
- Others
Let’s imagine that we have to do two simple things in JavaScript:
1. Check whether all elements in a table have a specific value
2. Filter a table
Standard approach
1)
var tab = [ 1, 1, 1]; var allTheSame = true; for(var i in tab) { if (tab[i] !== 1) { allTheSame = false; break; } }; console.log(allTheSame); // true
2)
var tab = [ 1, 1, 2]; var filtered = []; for(var i in tab) { if (tab[i] > 1) { filtered.push(tab[i]); } }; console.log(filtered); // [2]
It seems to be ok,but what can be improved?
Improvement 1 – built-in functions
ECMAScript 5 got new functionalities for tables such as every, some, forEach, map, filter, reduce
They are supported by all new browsers (IE>=9) http://kangax.github.io/compat-table/es5/
Now, our examples are as follows:
1)
var tab = [ 1, 1, 1]; var allTheSame = tab.every(function(el) { return el === 1; }); console.log(allTheSame);
2) filtering
var filtered = tab.filter(function(el) { return el > 1; }); console.log(filtered); // [2]
Improvement 2 – different libraries
If we want to support older browser or we want to have more opportunities we can take advantage from free libraries.
a) jQuery – has basic functionalities such as. .grep (filter), .each, .map, .is. Some of them are improved relative to those of ECMAScript 5, e.g. we can left .each using the return false, whereas we can also filter in the .map.
Record $.grep(tab, function(el, i) {} )
or $(tab).each(function(i, el) { })
b) Underscore – http://underscorejs.org/
Known library. Huge opportunities.
Here, you can find exemplary functions I used: findWhere (searching among objects), groupBy, sortBy, contains, isEmpty, pluck. We can form chains e.g.
_.chain(stooges) .sortBy(function(stooge){ return stooge.age; }) .map(function(stooge){ return stooge.name + ' is ' + stooge.age; }) .first() .value();
c) Lo-Dash – slightly improved clone of underscore
d) other
– functional.js: http://functionaljs.com/
e.g.
fjs.every(function (item) { return item % 2 === 0; })([1,2,3]);
– Rambda: http://ramda.github.io/ramdocs/docs/R.html#gt
e.g.
var double = function(x) { return x * 2; }; R.map(double, [1, 2, 3]); //=> [2, 4, 6]
– linq.js: http://linqjs.codeplex.com/ – almost 1:1 with Linq with .net
e.g.
Enumerable.Range(1, 10) .Where(function(i) { return i % 3 == 0; }) .Select(function(i) { return i * 10; })
Improvement 3 – auxiliary functions
To code was even shorter and easier to read, we can add auxiliary functions to compare values.
e.g.
var eq = function(valueToCompare) { return function(valueInList) { return valueInList === valueToCompare; }; };
And now, instead of
var allTheSame = tab.every(function(el) { return el === 1; });
we can write
var allTheSame = tab.every(eq(1));
Of course, we can add much more functions, such as gt, notEmpty, and, or etc.
We can also use library e.g.
f_underscore.js: http://krisjordan.github.io/f_underscore/#
e.g.
_.map(stooges, f_.toUpperCase(f_.get('name')));
Improvement 4 – lambdas
If you like lambdas, they are supported by libraries:
a) functional.js: http://functionaljs.com/basics/lambda/
e.g.
var doubleMap = fjs.map("n => n * 2");
b) linq.js
e.g.
var queryResult2 = Enumerable.From(jsonArray) .Where("$.user.id < 200") .OrderBy("$.user.screen_name") .Select("$.user.screen_name + ':' + $.text") .ToArray();
c) lambda.js – http://www.javascriptoo.com/lambda-js/
I add lambda function that takes a string and returns a function so that you can use it practically everywhere e.g.
var allTheSame = tab.every(lambda("== 1"));
of course, you can add something shorter than lambda
e.g.
L tab.every(L("== 1");
You can even extend the built-in functions to accept lambdas
["filter", "some", "every", "map"].forEach(function(fnName) { var oldF = Array.prototype[fnName]; Array.prototype[fnName] = function(fun) { var args = Array.prototype.slice.call(arguments); if (typeof fun == "string") { args[0] = lambda(fun); } return oldF.apply(this, args); }; });
If the first argument is a string, we change it to function using lambda.js library and pass it on.
And now we can do this:
[1,2,3].filter("> 1").map("x-> {key: x + 1}") //[{key: 3}, {key: 4}]
It couldn’t be simpler 🙂