Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Contribuez Discussion :

reduce mal connue, mal aimée


Sujet :

Contribuez

  1. #1
    Membre éprouvé
    reduce mal connue, mal aimée
    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/docs/Web/JavaScript/Reference/Objets_globaux/Array/reduce
    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


    Forcément, on se dit super ca sert à faire des sommes.

    Alors oui, avec des exemples comme ca, c'est réducteur, mais il faut bien lire la doc:

    Syntaxe
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    arr.reduce(callback)
    arr.reduce(callback, valeurInitiale)

    callback
    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.
    Donc la valeur de départ peut être n'importe quoi, par exemple un objet.

    2/ Imaginons cette structure de donnée:
    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, ...}, ...];

    Si je vous demande de récupérer le nombre total d'articles et le prix total, vous faites comment ?

    Vous faites plutôt 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);


    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}, ...];
    // 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);


    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.

    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 :
    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
    }), {})

    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
     
    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:
    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
    })


    Et vous, vous connaissez reduce ? vous l'utilisez ?
    on ne dit pas "ça ne marche pas" on dit "je suis incapable de faire fonctionner correctement les outils mis à ma disposition"
    Pas de question technique par MP

  2. #2
    Membre expert
    bravo, tu viens de decouvrir une nouvelle fonction...
    Citation Envoyé par gwyohm Voir le message
    Et vous, vous connaissez reduce ? vous l'utilisez ?
    oui & oui

    en lisant toute la doc, je pense que tu vas decouvrir un grand nombre de super fonctions que tu n'utilises/connais pas
    La forme des pyramides prouve que l'Homme a toujours tendance a en faire de moins en moins.

    Venez discuter sur le Chat de Développez !

###raw>template_hook.ano_emploi###