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

Zainteresowały Cię nasze treści?
Sprawdź co jeszcze przygotowaliśmy.

Adres e-mail

Dziękujemy! Na Twój adres e-mail wysłaliśmy prośbę o potwierdzenie zapisu do newslettera.

O nie! Coś poszło nie tak. Nie zapisałeś się.

Gdyby tylko dało się zapisać Twojego maila dwa razy :)

Niepoprawny mail. Spróbuj jeszcze raz.

Cookies

W pracy serwujemy suchar dnia. Tutaj musimy Cię poczęstować ciasteczkami. Dowiedz się więcej.