Je travaille beaucoup avec Vue, mais ma constatation est vraie pour tous ces frameworks, toutes ces librairies en vogue du moment :
on est souvent confronté à transformer nos données
On passe des réponses d'api http à notre store, de notre store à nos composants, entre nos composants.....
Je vois beaucoup de code fait avec des while, des for, des i, j, k, des copier coller merdiques... pour transformer les données, et moi j'aime pas.
De façon générale, je trouve que l'API offerte par Array est formidable et pas assez connue, et que combinée aux facilités d'écriture que nous offrent les outils tels que babel, webpack..., on peut quand même sortir de la grosse boucle C++ des familles.
Le forEach, filter et le map sont de plus en plus souvent utilisés, mais il y en a une méthode que je vois très peu utilisée et que je trouve pourtant formidable: reduce
1/ Le code sample dans la doc est certes peu attractif cf https://developer.mozilla.org/fr/doc...x/Array/reduce
Forcément, on se dit super ca sert à faire des sommes.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11 const array1 = [1, 2, 3, 4]; const reducer = (accumulator, currentValue) => accumulator + currentValue; // 1 + 2 + 3 + 4 console.log(array1.reduce(reducer)); // expected output: 10 // 5 + 1 + 2 + 3 + 4 console.log(array1.reduce(reducer, 5)); // expected output: 15
Alors oui, avec des exemples comme ca, c'est réducteur, mais il faut bien lire la doc:
Donc la valeur de départ peut être n'importe quoi, par exemple un objet.Syntaxe
callback
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2 arr.reduce(callback) arr.reduce(callback, valeurInitiale)
La fonction à exécuter sur chaque valeur de la liste (sauf le premier si aucune valeurInitiale n'est fournie), elle prend quatre arguments en entrée :
accumulateur
La valeur précédemment retournée par le dernier appel du callback, ou valeurInitiale, si elle est fournie (voir ci-après) (c'est la valeur « accumulée » au fur et à mesure des appels
valeurCourante
La valeur de l'élément courant actuellement manipulé dans le tableau.
...blabla
valeurInitialeFacultatif
Une valeur utilisée comme premier argument lors du premier appel de la fonction callback. Si aucune valeur initiale n'est fournie, le premier élément du tableau est utilisé (et la boucle de traitement ne le parcourera pas). Si on appelle reduce() sur un tableau vide sans fournir de valeur initiale, on aura une erreur.
2/ Imaginons cette structure de donnée:
Si je vous demande de récupérer le nombre total d'articles et le prix total, vous faites comment ?
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2 const shoppingCart = [{id: 1, unitPrice: 6, count: 5, name: 'jambon', ...}, {id: 2, count: 6, name: 'cornichon', unitPrice: 2, ...}, ...];
Vous faites plutôt un truc comme ca ?
Qui fait un truc comme ca:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11 const shoppingCart = [{id: 1, unitPrice: 4, count: 5, name: 'jambon', ...}, {id: 2, count: 6, name: 'cornichon', unitPrice: 2}, ...]; const res = { nbArticles: 0, totalPrice: 0 }; for (let i=0; i<shoppingCart.length; i++) { res.nbArticles += shoppingCart[i].count; res.totalPrice += shoppingCart[i].count * shoppingCart[i].unitPrice } console.log(res);
Je trouve que le type de code avec un reduce est moins risqué, et c'est d'autant plus vrai que les opérations sont complexes.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11 const shoppingCart = [{id: 1, unitPrice: 4, count: 5, name: 'jambon', ...}, {id: 2, count: 6, name: 'cornichon', unitPrice: 2}, ...]; // renvoyer le nombre total d'articles dans le panier et le prix du panier: const res = shoppingCart.reduce( ({ nbArticles, totalPrice }, { unitPrice = 0, count = 0 }) => ({ nbArticles: nbArticles + count, totalPrice: totalPrice + (count * unitPrice) }), {nbArticles: 0, totalPrice: 0} ) console.log(res);
3/ Changer le type des structures
Un autre cas ou j'utilise reduce, c'est pour passer d'un objet à un tableau ou d'un tableau à un objet :
L'inverse, passer d'un objet à un tableau ce fait naturellement avec les fonctions standard Object.entries, .values, .keys ...
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7 const shoppingCart = [{id: 1, unitPrice: 6, count: 5, name: 'jambon', ...}, {id: 2, count: 6, name: 'cornichon', unitPrice: 2, ...}, ...]; // obtenir un objet shoppingCart dont les clés sont l'id de chaque article dans le panier const shoppingMap = shopingCart.reduce((mapAcc, item) => ({ ...mapAcc, [item.id]: item }), {})
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4 return Object.values(shoppingMap); // si on veut également conserver la clé return Object.entries(shoppingMap).map(([key, value]) => ({ ...value, key })
Et après on peut encore faire d'autres trucs,
...la valeur de départ peut être n'importe quoi, par exemple une promesse.
Le use case est un peu différent, vous avez une liste de trucs, et pour chacun de ces trucs, vous devez charger ces données, mais disons que pour préserver votre serveur, vous décidez de toujours executer en séquentiel le traitement de votre tableau.
reduce est super pour ca:
Et vous, vous connaissez reduce ? vous l'utilisez ?
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11 // une liste de trucs pour lesquels je dois appliquer sequentiellement une à une tache asynchrone const things = [0, 1, 2, 3, 'aab', {}, ...]; things.reduce( // on fait un reduce, mais notre valeur de départ est une promesse résolue, donc (prev, thing) => prev.then( // quand la tache précédente est finie () => doSomethingWith(thing), // on renvoie une promesse avec l'élément suivant ), Promise.resolve() // la valeur de départ permet de démarrer le then initial ).then(() => { // console.log('fini') // quand on a fait la tache asynchrone })
Partager