Javascript – functional programming and lambdas

data: 22 grudnia, 2014
czas czytania: 4 min
autor: Grzegorz Kowalik

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) Underscorehttp://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.jshttp://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 🙂

Newsletter IT leaks

Dzielimy się inspiracjami i nowinkami z branży IT. Szanujemy Twój czas - obiecujemy nie spamować i wysyłać wiadomości raz na dwa miesiące.

Subscribe to our newsletter

Administratorem Twoich danych osobowych jest Future Processing S.A. z siedzibą w Gliwicach. Twoje dane będziemy przetwarzać w celu przesyłania cyklicznego newslettera dot. branży IT. W każdej chwili możesz się wypisać lub edytować swoje dane. Więcej informacji znajdziesz w naszej polityce prywatności.

Subscribe to our newsletter

Administratorem Twoich danych osobowych jest Future Processing S.A. z siedzibą w Gliwicach. Twoje dane będziemy przetwarzać w celu przesyłania cyklicznego newslettera dot. branży IT. W każdej chwili możesz się wypisać lub edytować swoje dane. Więcej informacji znajdziesz w naszej polityce prywatności.