Programmation fonctionnelle en JavaScript Loïc Knuchel
The Obvious
“La programmation fonctionnelle est une manière de programmer principalement basée sur des fonctions”
The Headache
“La programmation fonctionnelle est un style de développement qui promeut les fonctions indépendantes de l’état du programme.”
The One
“La programmation fonctionnelle permet de coder de manière plus modulaire et plus productive, avec moins de code et moins de bugs”
Transformer un tableauvar names = ['Finn', 'Rey', 'Poe', 'Kaylo'];
function upperCaseArray(arr){
var ret = [];
for(var i=0; i<arr.length; i++){
ret[i] = arr[i].toUpperCase();
}
return ret;
}
console.log(upperCaseArray(names));
// ['FINN', 'REY', 'POE', 'KAYLO']
Transformer un tableauvar names = ['Finn', 'Rey', 'Poe', 'Kaylo'];
function upperCaseArray(arr){
var ret = [];
for(var i=0; i<arr.length; i++){
ret[i] = arr[i].toUpperCase();
}
return ret;
}
console.log(upperCaseArray(names));
// ['FINN', 'REY', 'POE', 'KAYLO']
function upperCaseArray(arr){
return arr.map(function(item){
return item.toUpperCase();
});
}
Créer son .map()
Array.prototype.map = function(callback){
var array = this;
var result = [];
for(var i=0; i<array.length; i++){
result[i] = callback(array[i]);
}
return result;
};
Traiter des données complexesvar claims = [{ wan: '123', actions: [ {name: 'sendPicture', pictures: [ {path: '123/1.jpg', deleted: true, sync: false}, {path: '123/2.jpg', deleted: false, sync: true}, ]}, {name: 'changeStep', step: 'COM'}, {name: 'sendPicture', pictures: [ {path: '123/3.jpg', deleted: false, sync: true}, {path: '123/4.jpg', deleted: false, sync: true} ]}, {name: 'sendPicture', pictures: [ {path: '123/5.jpg', deleted: true, sync: false}, {path: '123/6.jpg', deleted: false, sync: false} ]} ]}, { wan: '456', actions: [ {name: 'sendPicture', pictures: [ {path: '456/1.jpg', deleted: false, sync: true}, {path: '456/2.jpg', deleted: false, sync: true}, ]}, {name: 'sendPicture', pictures: [ {path: '123/3.jpg', deleted: true, sync: false}, {path: '123/4.jpg', deleted: true, sync: false} ]} ]}];
Traiter des données complexesvar claims = [{ wan: '123', actions: [ {name: 'sendPicture', pictures: [ {path: '123/1.jpg', deleted: true, sync: false}, {path: '123/2.jpg', deleted: false, sync: true}, ]}, {name: 'changeStep', step: 'COM'}, {name: 'sendPicture', pictures: [ {path: '123/3.jpg', deleted: false, sync: true}, {path: '123/4.jpg', deleted: false, sync: true} ]}, {name: 'sendPicture', pictures: [ {path: '123/5.jpg', deleted: true, sync: false}, {path: '123/6.jpg', deleted: false, sync: false} ]} ]}, { wan: '456', actions: [ {name: 'sendPicture', pictures: [ {path: '456/1.jpg', deleted: false, sync: true}, {path: '456/2.jpg', deleted: false, sync: true}, ]}, {name: 'sendPicture', pictures: [ {path: '123/3.jpg', deleted: true, sync: false}, {path: '123/4.jpg', deleted: true, sync: false} ]} ]}];
function doSomething(claims){
var pictures = [];
for(var i=0; i<claims.length; i++){
var claim = claims[i];
for(var j=0; j<claim.actions.length; j++){
var action = claim.actions[j];
if(action.name === 'sendPicture'){
for(var k=0; k<action.pictures.length; k++){
var picture = action.pictures[k];
if(!picture.deleted && !picture.sync){
pictures.push(picture);
}
}
}
}
}
return pictures;
}
Traiter des données complexesvar claims = [{ wan: '123', actions: [ {name: 'sendPicture', pictures: [ {path: '123/1.jpg', deleted: true, sync: false}, {path: '123/2.jpg', deleted: false, sync: true}, ]}, {name: 'changeStep', step: 'COM'}, {name: 'sendPicture', pictures: [ {path: '123/3.jpg', deleted: false, sync: true}, {path: '123/4.jpg', deleted: false, sync: true} ]}, {name: 'sendPicture', pictures: [ {path: '123/5.jpg', deleted: true, sync: false}, {path: '123/6.jpg', deleted: false, sync: false} ]} ]}, { wan: '456', actions: [ {name: 'sendPicture', pictures: [ {path: '456/1.jpg', deleted: false, sync: true}, {path: '456/2.jpg', deleted: false, sync: true}, ]}, {name: 'sendPicture', pictures: [ {path: '123/3.jpg', deleted: true, sync: false}, {path: '123/4.jpg', deleted: true, sync: false} ]} ]}];
function doSomething(claims){
var pictures = [];
for(var i=0; i<claims.length; i++){
var claim = claims[i];
for(var j=0; j<claim.actions.length; j++){
var action = claim.actions[j];
if(action.name === 'sendPicture'){
for(var k=0; k<action.pictures.length; k++){
var picture = action.pictures[k];
if(!picture.deleted && !picture.sync){
pictures.push(picture);
}
}
}
}
}
return pictures;
}
Traiter des données complexes (lodash)
function getPicturesToSync(claims){
var actions = _.flatten(_.map(claims, function(claim){ return claim.actions; }));
var pictures = _.flatten(_.map(_.filter(actions, function(action){ return action.name === 'sendPicture'; }), function
(action){ return action.pictures; }));
return _.filter(pictures, function(picture){ return picture.deleted === false && picture.sync === false;});
}
Traiter des données complexes (lodash)
function getPicturesToSync(claims){
var actions = _.flatten(_.map(claims, function(claim){ return claim.actions; }));
var pictures = _.flatten(_.map(_.filter(actions, function(action){ return action.name === 'sendPicture'; }), function
(action){ return action.pictures; }));
return _.filter(pictures, function(picture){ return picture.deleted === false && picture.sync === false;});
}
_.map(array, callback) crée un tableau avec les valeurs retournées par le callback en paramètre.
Traiter des données complexes (lodash)
function getPicturesToSync(claims){
var actions = _.flatten(_.map(claims, function(claim){ return claim.actions; }));
var pictures = _.flatten(_.map(_.filter(actions, function(action){ return action.name === 'sendPicture'; }), function
(action){ return action.pictures; }));
return _.filter(pictures, function(picture){ return picture.deleted === false && picture.sync === false;});
}
_.flatten(array) crée un tableau simple à partir d’un tableau de tableaux.
Traiter des données complexes (lodash)
function getPicturesToSync(claims){
var actions = _.flatten(_.map(claims, function(claim){ return claim.actions; }));
var pictures = _.flatten(_.map(_.filter(actions, function(action){ return action.name === 'sendPicture'; }), function
(action){ return action.pictures; }));
return _.filter(pictures, function(picture){ return picture.deleted === false && picture.sync === false;});
}
_.filter(array, callback) crée un tableau en gardant que les éléments pour lesquels le callback renvoi true.
Traiter des données complexes (lodash)
function getPicturesToSync(claims){
var actions = _.flatten(_.map(claims, function(claim){ return claim.actions; }));
var pictures = _.flatten(_.map(_.filter(actions, function(action){ return action.name === 'sendPicture'; }), function
(action){ return action.pictures; }));
return _.filter(pictures, function(picture){ return picture.deleted === false && picture.sync === false;});
}
Traiter des données complexes (lodash)
function getPicturesToSync(claims){
var actions = _.flatten(_.map(claims, function(claim){ return claim.actions; }));
var pictures = _.flatten(_.map(_.filter(actions, function(action){ return action.name === 'sendPicture'; }), function
(action){ return action.pictures; }));
return _.filter(pictures, function(picture){ return picture.deleted === false && picture.sync === false;});
}
Traiter des données complexes (lodash)
function getPicturesToSync(claims){
var actions = _.flatten(_.map(claims, function(claim){ return claim.actions; }));
var pictures = _.flatten(_.map(_.filter(actions, function(action){ return action.name === 'sendPicture'; }), function
(action){ return action.pictures; }));
return _.filter(pictures, function(picture){ return picture.deleted === false && picture.sync === false;});
}
Traiter des données complexes (lodash)
function getPicturesToSync(claims){
var actions = _.flatten(_.map(claims, function(claim){ return claim.actions; }));
var pictures = _.flatten(_.map(_.filter(actions, function(action){ return action.name === 'sendPicture'; }), function
(action){ return action.pictures; }));
return _.filter(pictures, function(picture){ return picture.deleted === false && picture.sync === false;});
}
Améliorer les tableaux JavaScript
Array.prototype.find = function(callback){ return _.find(this, callback); }
Array.prototype.filter = function(callback){ return _.filter(this, callback); }
Array.prototype.map = function(callback){ return _.map(this, callback); }
Array.prototype.flatten = function() { return _.flatten(this); }
Traiter des données complexes (js array)
function getPicturesToSync(claims){
return claims
.map(function(claim){ return claim.actions; })
.flatten()
.filter(function(action){ return action.name === 'sendPicture'; })
.map(function(action){ return action.pictures; })
.flatten()
.filter(function(picture){ return picture.deleted === false && picture.sync === false;});
}
Traiter des données complexes (es6 fat arrow)
function getPicturesToSync(claims){
return claims
.map(claim => claim.actions)
.flatten()
.filter(action => action.name === 'sendPicture')
.map(action => action.pictures)
.flatten()
.filter(picture => picture.deleted === false && picture.sync === false);
}
Traiter des données complexes (pluck)
function getPicturesToSync(claims){
return claims
.map('actions')
.flatten()
.filter({name: 'sendPicture'})
.map('pictures')
.flatten()
.filter({deleted: false, sync: false});
}
Traiter des données complexes (flatMap)
var data = [
{id: '1', values: [1, 2, 3]},
{id: '2', values: [4, 5]},
];
data.map('values'); // [[1, 2, 3], [4, 5]]
data.map('values').flatten(); // [1, 2, 3, 4, 5]
Traiter des données complexes (flatMap)
Array.prototype.flatMap = function(callback){ return _.flatten(_.map(this, callback)); }
var data = [
{id: '1', values: [1, 2, 3]},
{id: '2', values: [4, 5]},
];
data.map('values'); // [[1, 2, 3], [4, 5]]
data.map('values').flatten(); // [1, 2, 3, 4, 5]
data.flatMap('values'); // [1, 2, 3, 4, 5]
Traiter des données complexes (flatMap)
function getPicturesToSync(claims){
return claims
.flatMap('actions')
.filter({name: 'sendPicture'})
.flatMap('pictures')
.filter({deleted: false, sync: false});
}
function getPicturesToSync(claims){
return claims
.flatMap('actions')
.filter({name: 'sendPicture'})
.flatMap('pictures')
.filter({deleted: false, sync: false});
}
Bilanfunction getPicturesToSync(claims){
var pictures = [];
for(var i=0; i<claims.length; i++){
var claim = claims[i];
for(var j=0; j<claim.actions.length; j++){
var action = claim.actions[j];
if(action.name === 'sendPicture'){
for(var k=0; k<action.pictures.length; k++){
var picture = action.pictures[k];
if(!picture.deleted && !picture.sync){
pictures.push(picture);
}
}
}
}
}
return pictures;
}
function getPicturesToSync(claims){
return claims
.flatMap('actions')
.filter({name: 'sendPicture'})
.flatMap('pictures')
.filter({deleted: false, sync: false});
}
● moins de code● moins de bugs● plus de productivité
Bilanfunction getPicturesToSync(claims){
var pictures = [];
for(var i=0; i<claims.length; i++){
var claim = claims[i];
for(var j=0; j<claim.actions.length; j++){
var action = claim.actions[j];
if(action.name === 'sendPicture'){
for(var k=0; k<action.pictures.length; k++){
var picture = action.pictures[k];
if(!picture.deleted && !picture.sync){
pictures.push(picture);
}
}
}
}
}
return pictures;
}
Autre exemplevar claims = [{ wan: '123', actions: [ {name: 'sendPicture', pictures: [ {path: '123/1.jpg', deleted: true, sync: false}, {path: '123/2.jpg', deleted: false, sync: true}, ]}, {name: 'changeStep', step: 'COM'}, {name: 'sendPicture', pictures: [ {path: '123/3.jpg', deleted: false, sync: true}, {path: '123/4.jpg', deleted: false, sync: true} ]}, {name: 'sendPicture', pictures: [ {path: '123/5.jpg', deleted: true, sync: false}, {path: '123/6.jpg', deleted: false, sync: false} ]} ]}, { wan: '456', actions: [ {name: 'sendPicture', pictures: [ {path: '456/1.jpg', deleted: false, sync: true}, {path: '456/2.jpg', deleted: false, sync: true}, ]}, {name: 'sendPicture', pictures: [ {path: '123/3.jpg', deleted: true, sync: false}, {path: '123/4.jpg', deleted: true, sync: false} ]} ]}];
function doSomething(claims, wan){
return claims
.find({wan: wan}).actions
.filter({name: 'sendPicture'})
.flatMap('pictures')
.filter({deleted: false});
}
Autre exemplevar claims = [{ wan: '123', actions: [ {name: 'sendPicture', pictures: [ {path: '123/1.jpg', deleted: true, sync: false}, {path: '123/2.jpg', deleted: false, sync: true}, ]}, {name: 'changeStep', step: 'COM'}, {name: 'sendPicture', pictures: [ {path: '123/3.jpg', deleted: false, sync: true}, {path: '123/4.jpg', deleted: false, sync: true} ]}, {name: 'sendPicture', pictures: [ {path: '123/5.jpg', deleted: true, sync: false}, {path: '123/6.jpg', deleted: false, sync: false} ]} ]}, { wan: '456', actions: [ {name: 'sendPicture', pictures: [ {path: '456/1.jpg', deleted: false, sync: true}, {path: '456/2.jpg', deleted: false, sync: true}, ]}, {name: 'sendPicture', pictures: [ {path: '123/3.jpg', deleted: true, sync: false}, {path: '123/4.jpg', deleted: true, sync: false} ]} ]}];
function doSomething(claims, wan){
return claims
.find({wan: wan}).actions
.filter({name: 'sendPicture'})
.flatMap('pictures')
.filter({deleted: false});
}
Bilanfunction getPictures(claims, wan){
for(var i=0; i<claims.length; i++){
var claim = claims[i];
if(claim.wan === wan){
var pictures = [];
for(var j=0; j<=claim.actions.length; j++){
var action = claim.actions[i];
if(action.name === 'sendPicture'){
for(var k=0; k<action.pictures.length; k++){
var picture = action.pictures[k];
if(picture.deleted){
pictures.push(picture);
}
}
}
}
return pictures;
}
}
}
function getPictures(claims, wan){
return claims
.find({wan: wan}).actions
.filter({name: 'sendPicture'})
.flatMap('pictures')
.filter({deleted: false});
}
Bilanfunction getPictures(claims, wan){
for(var i=0; i<claims.length; i++){
var claim = claims[i];
if(claim.wan === wan){
var pictures = [];
for(var j=0; j<=claim.actions.length; j++){
var action = claim.actions[i];
if(action.name === 'sendPicture'){
for(var k=0; k<action.pictures.length; k++){
var picture = action.pictures[k];
if(picture.deleted){
pictures.push(picture);
}
}
}
}
return pictures;
}
}
}
function getPictures(claims, wan){
return claims
.find({wan: wan}).actions
.filter({name: 'sendPicture'})
.flatMap('pictures')
.filter({deleted: false});
}
Bilan (correct)function getPictures(claims, wan){
for(var i=0; i<claims.length; i++){
var claim = claims[i];
if(claim.wan === wan){
var pictures = [];
for(var j=0; j<claim.actions.length; j++){
var action = claim.actions[j];
if(action.name === 'sendPicture'){
for(var k=0; k<action.pictures.length; k++){
var picture = action.pictures[k];
if(!picture.deleted){
pictures.push(picture);
}
}
}
}
return pictures;
}
}
}
function getPictures(claims, wan){
return claims
.find({wan: wan}).actions
.filter({name: 'sendPicture'})
.flatMap('pictures')
.filter({deleted: false});
}
BilanLa programmation fonctionnelle permet de déclarer des intentions.
function getPictures(claims, wan){
return claims
.find({wan: wan}).actions
.filter({name: 'sendPicture'})
.flatMap('pictures')
.filter({deleted: false});
}
function getPicturesToSync(claims){
return claims
.flatMap('actions')
.filter({name: 'sendPicture'})
.flatMap('pictures')
.filter({deleted: false, sync: false});
}
BilanLa programmation fonctionnelle permet de déclarer des intentions.
Au final, on ne sait pas vraiment ce qui est fait et quand.
function getPictures(claims, wan){
return claims
.find({wan: wan}).actions
.filter({name: 'sendPicture'})
.flatMap('pictures')
.filter({deleted: false});
}
function getPicturesToSync(claims){
return claims
.flatMap('actions')
.filter({name: 'sendPicture'})
.flatMap('pictures')
.filter({deleted: false, sync: false});
}
BilanLa programmation fonctionnelle permet de déclarer des intentions.
Au final, on ne sait pas vraiment ce qui est fait et quand.
Ce qui permet de changer le fonctionnement du programme sans changer le code.
function getPictures(claims, wan){
return claims
.find({wan: wan}).actions
.filter({name: 'sendPicture'})
.flatMap('pictures')
.filter({deleted: false});
}
function getPicturesToSync(claims){
return claims
.flatMap('actions')
.filter({name: 'sendPicture'})
.flatMap('pictures')
.filter({deleted: false, sync: false});
}
BilanLa programmation fonctionnelle permet de déclarer des intentions.
Au final, on ne sait pas vraiment ce qui est fait et quand.
Ce qui permet de changer le fonctionnement du programme sans changer le code.
Ex:
● synchrone => asynchrone● impératif => lazy (cf Lazy.js)
function getPictures(claims, wan){
return claims
.find({wan: wan}).actions
.filter({name: 'sendPicture'})
.flatMap('pictures')
.filter({deleted: false});
}
function getPicturesToSync(claims){
return claims
.flatMap('actions')
.filter({name: 'sendPicture'})
.flatMap('pictures')
.filter({deleted: false, sync: false});
}
Quelques autres fonctions de lodash● groupBy _.groupBy(claims[0].actions, function(action){
return action.name;
});
/* Result :
{
'sendPicture': [
{name: 'sendPicture', pictures: [...]},
{name: 'sendPicture', pictures: [...]},
{name: 'sendPicture', pictures: [...]}
],
'changeStep': [
{name: 'changeStep', step: 'COM'}
]
}
*/
Quelques autres fonctions de lodash● groupBy● partition
_.partition([1, 2, 3], function(n){
return n % 2;
});
// Result : [[1, 3], [2]]
Quelques autres fonctions de lodash● groupBy● partition● sortBy
_.sortBy([2, 3, 1], function(n){
return n;
});
// Result : [1, 2, 3]
Quelques autres fonctions de lodash● groupBy● partition● sortBy● take / drop
_.take([1, 2, 3, 4, 5], 3); // Result : [1, 2, 3]
_.drop([1, 2, 3, 4, 5], 1); // Result : [2, 3, 4, 5]
Quelques autres fonctions de lodash● groupBy● partition● sortBy● take / drop● uniq
_.uniq(['foo', 'bar', 'foo', 'foo']);
// Result : ['foo', 'bar']
_.uniq([{wan: '1'}, {wan: '2'}, {wan: '1'}], 'wan');
// Result : [{wan: '1'}, {wan: '2'}]
Quelques autres fonctions de lodash● groupBy● partition● sortBy● take / drop● uniq● reduce
_.reduce(claims, function(count, claim){
return count + claim.actions.length;
}, 0);
// Result: 6
Quelques autres fonctions de lodash● groupBy● partition● sortBy● take / drop● uniq● reduce● sum / min / max
_.sum(['Finn', 'Rey', 'Poe', 'Kaylo'], function(name){
return name.length;
});
// Result : 15
Quelques autres fonctions de lodash● groupBy● partition● sortBy● take / drop● uniq● reduce● sum / min / max● ...
No side effectEffets de bord: lancer une exception/erreur, faire un appel (bdd, http, fichier…), récupérer la date
actuelle, modifier un paramètre, accéder à une variable “globale”, mettre un log
Bénéfices
● Moins de code (~÷3 par rapport à Java)
● Plus compréhensible
● Plus facile à réutiliser / composer
● Plus facile à tester
● Moins de bugs
Exemple: Afficher ces données dans un graphvar data = [
{
name: "Jamestown",
population: 2047,
temperatures: [-34, 67, 101, 87]
},
{
name: "Awesome Town",
population: 3568,
temperatures: [-3, 4, 9, 12]
},
{
name: "Funky Town",
population: 1000000,
temperatures: [75, 75, 75, 75, 75]
}
];
// Result :
[
[55.25, 2047], // [average temperature, population]
[5.5, 3568],
[75, 1000000]
]
Code impératiffunction formatChart(data){
var coords = [],
totalTemp = 0,
averageTemp = 0;
for(var i=0; i < data.length; i++){
totalTemp = 0;
for(var j=0; j < data[i].temperatures.length; j++){
totalTemp += data[i].temperatures[j];
}
averageTemp = totalTemp / data[i].temperatures.length;
coords.push([averageTemp, data[i].population]);
}
return coords;
}
Code impératiffunction formatChart(data){
var coords = [],
totalTemp = 0,
averageTemp = 0;
for(var i=0; i < data.length; i++){
totalTemp = 0;
for(var j=0; j < data[i].temperatures.length; j++){
totalTemp += data[i].temperatures[j];
}
averageTemp = totalTemp / data[i].temperatures.length;
coords.push([averageTemp, data[i].population]);
}
return coords;
}
● Pas réutilisable
● Difficile à comprendre
● bugs probables
Functionnal way : sommer les températuresvar totalTemp = totalForArray(0, temperatures);
// recursive to avoid loop
function totalForArray(currentTotal, arr){
if(arr.length === 0){
return currentTotal;
} else {
return totalForArray(currentTotal + arr[0], arr.slice(1));
}
}
Functionnal way : sommer les températuresvar totalTemp = totalForArray(0, temperatures);
// recursive to avoid loop
function totalForArray(currentTotal, arr){
if(arr.length === 0){
return currentTotal;
} else {
return totalForArray(currentTotal + arr[0], arr.slice(1));
}
}
Vs
function totalForArray(currentTotal, arr){
return arr.reduce((total, item) => total + item, currentTotal);
}
Functionnal way : calculer la température moyennefunction average(total, count){
return total / count;
}
Functionnal way : calculer la température moyennefunction average(total, count){
return total / count;
}
function averageForArray(arr){
return average(totalForArray(0, arr), arr.length);
}
Functionnal way : calculer la température moyennefunction average(total, count){
return total / count;
}
function averageForArray(arr){
return average(totalForArray(0, arr), arr.length);
}
var averageTemp = averageForArray(temperatures);
Functionnal way : récupérer les températuresvar allTemperatures = data.map(function(item){
return item.temperatures;
});
var data = [
{
name: "Jamestown",
population: 2047,
temperatures: [-34, 67, 101, 87]
},
{
name: "Awesome Town",
population: 3568,
temperatures: [-3, 4, 9, 12]
},
{
name: "Funky Town",
population: 1000000,
temperatures: [75, 75, 75, 75, 75]
}
];
Functionnal way : récupérer les températuresvar allTemperatures = data.map(function(item){
return item.temperatures;
});
// simplify & shorten map syntax
function getItem(propertyName){
return function(item){
return item[propertyName];
}
}
var allTemperatures = data.map(getItem('temperature'));
var data = [
{
name: "Jamestown",
population: 2047,
temperatures: [-34, 67, 101, 87]
},
{
name: "Awesome Town",
population: 3568,
temperatures: [-3, 4, 9, 12]
},
{
name: "Funky Town",
population: 1000000,
temperatures: [75, 75, 75, 75, 75]
}
];
Curryficationfunction add1(b){
return 1+b;
}
console.log(add1(2)); // 3
function addCurry(a){
return function(b){
return a+b;
}
}
var add1 = addCurry(1);
var add2 = addCurry(2);
console.log(add1(2)); // 3
console.log(add2(2)); // 4
Functionnal way : récupérer les températuresvar allTemperatures = data.map(function(item){
return item.temperatures;
});
// simplify & shorten map syntax
function getItem(propertyName){
return function(item){
return item[propertyName];
}
}
var allTemperatures = data.map(getItem('temperature'));
var data = [
{
name: "Jamestown",
population: 2047,
temperatures: [-34, 67, 101, 87]
},
{
name: "Awesome Town",
population: 3568,
temperatures: [-3, 4, 9, 12]
},
{
name: "Funky Town",
population: 1000000,
temperatures: [75, 75, 75, 75, 75]
}
];
Functionnal way : récupérer les températuresvar allTemperatures = data.map(function(item){
return item.temperatures;
});
// simplify & shorten map syntax
function getItem(propertyName){
return function(item){
return item[propertyName];
}
}
var allTemperatures = data.map(getItem('temperature'));
// more concise again !
function pluck(arr, propertyName){
return arr.map(getItem(propertyName));
}
var allTemperatures = pluck(data, 'temperatures');
var data = [
{
name: "Jamestown",
population: 2047,
temperatures: [-34, 67, 101, 87]
},
{
name: "Awesome Town",
population: 3568,
temperatures: [-3, 4, 9, 12]
},
{
name: "Funky Town",
population: 1000000,
temperatures: [75, 75, 75, 75, 75]
}
];
Functionnal way : combiner nos donnéesvar populations = pluck(data, 'population'); // [2047, 3568, 1000000]
var averageTemps = pluck(data, 'temperatures').map(averageForArray); // [55.25, 5.5, 75]
Functionnal way : combiner nos donnéesvar populations = pluck(data, 'population'); // [2047, 3568, 1000000]
var averageTemps = pluck(data, 'temperatures').map(averageForArray); // [55.25, 5.5, 75]
function combineArrays(arr1, arr2, resultArr){
resultArr = resultArr || [];
if(arr1.length === 0 || arr2.length === 0){
return resultArr;
} else {
return combineArrays(arr1.slice(1), arr2.slice(1), resultArr.push([arr1[0], arr2[0]]));
}
}
var chartData = combineArrays(averageTemps, populations);
Functionnal wayfunction formatChart(data){
return combineArrays(pluck(data, 'temperatures').map(averageForArray), pluck(data, 'population'));
}
Functionnal wayfunction formatChart(data){
return combineArrays(pluck(data, 'temperatures').map(averageForArray), pluck(data, 'population'));
}
Vs
function formatChart(data){
var coords = [],
totalTemp = 0,
averageTemp = 0;
for(var i=0; i < data.length; i++){
totalTemp = 0;
for(var j=0; j < data[i].temperatures.length; j++){
totalTemp += data[i].temperatures[j];
}
averageTemp = totalTemp / data[i].temperatures.length;
coords.push([averageTemp, data[i].population]);
}
return coords;
}
Functionnal way
function formatChart(data){
return _.zip(_.pluck(data, 'temperatures').map(t => _.sum(t) / t.length), _.pluck(data, 'population'));
}
Quel est le problème ?function getName(user){
return user.name;
}
getName(); // ERROR: Cannot read property 'name' of undefined
Quel est le problème ?function getName(user){
return user.name;
}
getName(); // ERROR: Cannot read property 'name' of undefined
getName(localStorage.getItem('user')); // ERROR ???
Option (scala)def getName(user: User): String {
return user.name
}
def getName(userOpt: Option[User]): String {
return userOpt.map(user => user.name).getOrElse("")
}
Monads
● Wrapper (context) M[A]
● Fonction map def map[A, B](f: A => B): M[A] => M[B]
● Fonction flatMap def flatMap[A, B](f: A => M[B]): M[A] => M[B]
Conclusion● passer toutes les données nécessaires en paramètre● ne pas les modifier● ne pas utiliser null, les exceptions, les boucles● utiliser le moins possible les if● faire des fonctions très simples et les composer● utiliser des fonctions d’ordre supérieur
The One
“La programmation fonctionnelle permet de coder de manière plus modulaire et plus productive, avec moins de code et moins de bugs”