Principes de programmation fonctionnelle en JavaScript

L'auteur du matériel, dont nous publions la traduction aujourd'hui, dit qu'après une longue période de programmation orientée objet, il a réfléchi à la complexité des systèmes. Selon John Ousterhout , la complexité est tout ce qui rend la compréhension ou la modification des logiciels plus difficile. L'auteur de cet article, après avoir effectué quelques recherches, a découvert les concepts de programmation fonctionnelle comme l'immunité et les fonctions pures. L'utilisation de tels concepts vous permet de créer des fonctions qui n'ont pas d'effets secondaires. L'utilisation de ces fonctions simplifie la prise en charge du système et offre au programmeur d'autres avantages .

image

Nous parlons ici de programmation fonctionnelle et de certains de ses principes importants. Tout cela sera illustré par de nombreux exemples de code JavaScript.

Qu'est-ce que la programmation fonctionnelle?


Vous pouvez en savoir plus sur la programmation fonctionnelle sur Wikipedia . À savoir, nous parlons du fait que la programmation fonctionnelle est un paradigme de programmation dans lequel le processus de calcul est traité comme calculant les valeurs des fonctions dans la compréhension mathématique de ces dernières. La programmation fonctionnelle implique de faire le calcul des résultats des fonctions à partir des données source et des résultats d'autres fonctions, et n'implique pas le stockage explicite de l'état du programme. Par conséquent, cela n'implique pas la variabilité de cet état.

Maintenant, à titre d'exemples, nous allons analyser quelques idées de programmation fonctionnelle.

Fonctions pures


Les fonctions pures sont le premier concept fondamental à étudier pour comprendre l'essence de la programmation fonctionnelle.

Qu'est-ce qu'une «fonction pure»? Qu'est-ce qui rend une fonction «propre»? Une fonction pure doit répondre aux exigences suivantes:

  • Il renvoie toujours, en lui passant les mêmes arguments, le même résultat (de telles fonctions sont aussi appelées déterministes).
  • Une telle fonction n'a pas d'effets secondaires.

Considérez la première propriété des fonctions pures, à savoir le fait qu'elles, en leur passant les mêmes arguments, retournent toujours le même résultat.

▍ Arguments de fonction et valeurs de retour


Imaginez que nous devons créer une fonction qui calcule l'aire d'un cercle. Une fonction qui n'est pas pure prendrait comme paramètre le rayon du cercle ( radius ), après quoi elle renverrait la valeur du calcul de l'expression radius * radius * PI :

 const PI = 3.14; function calculateArea(radius) { return radius * radius * PI; } calculateArea(10); //  314 

Pourquoi cette fonction ne peut-elle pas être appelée pure? Le fait est qu'il utilise une constante globale, qui ne lui est pas transmise comme argument.

Imaginez maintenant que certains mathématiciens sont arrivés à la conclusion que la valeur de la constante PI devrait être le nombre 42 , à cause de quoi la valeur de cette constante a été modifiée.

Maintenant, une fonction qui n'est pas pure, lorsqu'elle reçoit la même valeur d'entrée, le nombre 10 , renverra la valeur 10 * 10 * 42 = 4200 . Il s'avère qu'en utilisant ici la même chose que dans l'exemple précédent, la valeur du paramètre radius , la fonction renvoie un résultat différent. Corrigeons ceci:

 const PI = 3.14; function calculateArea(radius, pi) { return radius * radius * pi; } calculateArea(10, PI); //  314 

Maintenant, lors de l'appel de cette fonction, nous lui passerons toujours l'argument pi . Par conséquent, la fonction ne fonctionnera qu'avec ce qui lui est transmis lors de son appel, sans recourir à des entités globales. Si nous analysons le comportement de cette fonction, nous pouvons arriver aux conclusions suivantes:

  • Si les fonctions passent l'argument de radius égal à 10 et l'argument pi égal à 3.14 , il retournera toujours le même résultat - 314 .
  • Lorsqu'il est appelé avec un argument de radius de 10 et pi de 42 , il retournera toujours 4200 .

Lecture de fichiers


Si notre fonction lit des fichiers, elle ne sera pas propre. Le fait est que le contenu des fichiers peut changer.

 function charactersCounter(text) { return `Character count: ${text.length}`; } function analyzeFile(filename) { let fileContent = open(filename); return charactersCounter(fileContent); } 

Génération de nombres aléatoires


Toute fonction qui repose sur un générateur de nombres aléatoires ne peut pas être pure.

 function yearEndEvaluation() { if (Math.random() > 0.5) {   return "You get a raise!"; } else {   return "Better luck next year!"; } } 

Parlons maintenant des effets secondaires.

▍ Effets secondaires


Un exemple d'un effet secondaire qui peut se produire lorsqu'une fonction est appelée est la modification de variables globales ou d'arguments passés aux fonctions par référence.

Supposons que nous devions créer une fonction qui prend un entier et incrémente ce nombre par 1. Voici à quoi pourrait ressembler une implémentation d'une idée similaire:

 let counter = 1; function increaseCounter(value) { counter = value + 1; } increaseCounter(counter); console.log(counter); // 2 

Il existe un counter variables globales. Notre fonction, qui n'est pas pure, reçoit cette valeur en argument et la remplace, en ajoutant une à sa valeur précédente.

La variable globale change, similaire dans la programmation fonctionnelle n'est pas la bienvenue.

Dans notre cas, la valeur de la variable globale est modifiée. Comment nettoyer la fonction d' increaseCounter() dans ces conditions? En fait, c'est très simple:

 let counter = 1; function increaseCounter(value) { return value + 1; } increaseCounter(counter); // 2 console.log(counter); // 1 

Comme vous pouvez le voir, la fonction renvoie 2 , mais la valeur du counter variable globale ne change pas. Ici, nous pouvons conclure que la fonction renvoie la valeur qui lui est passée, augmentée de 1 , sans rien changer.

Si vous suivez les deux règles ci-dessus pour écrire des fonctions pures, cela facilitera la navigation dans les programmes créés à l'aide de ces fonctions. Il s'avère que chaque fonction sera isolée et n'affectera pas les parties du programme qui lui sont extérieures.

Les fonctions pures sont stables, cohérentes et prévisibles. En recevant les mêmes données d'entrée, ces fonctions renvoient toujours le même résultat. Cela évite au programmeur d'essayer de prendre en compte la possibilité de situations dans lesquelles le transfert de fonctions des mêmes paramètres conduit à des résultats différents, car cela est tout simplement impossible avec des fonctions pures.

▍ Forces des fonctions pures


Parmi les points forts des fonctions pures, il y a le fait que le code écrit en les utilisant est plus facile à tester. En particulier, vous n'avez pas besoin de créer d'objets de stub. Cela permet des tests unitaires de fonctions pures dans divers contextes:

  • Si le paramètre A est transmis à la fonction, la valeur de retour de B est attendue.
  • Si le paramètre C est transmis à la fonction, la valeur de retour de D est attendue.

Comme exemple simple de cette idée, nous pouvons donner une fonction qui prend un tableau de nombres, et on s'attend à ce qu'elle augmente d'un chaque numéro de ce tableau, renvoyant un nouveau tableau avec les résultats:

 let list = [1, 2, 3, 4, 5]; function incrementNumbers(list) { return list.map(number => number + 1); } 

Ici, nous passons un tableau de nombres à la fonction, après quoi nous utilisons la méthode tableau map() , qui nous permet de modifier chaque élément du tableau et forme un nouveau tableau renvoyé par la fonction. Nous appelons la fonction en lui passant un tableau de list :

 incrementNumbers(list); //  [2, 3, 4, 5, 6] 

De cette fonction, il est prévu que, après avoir accepté un tableau de la forme [1, 2, 3, 4, 5] , il retournera un nouveau tableau [2, 3, 4, 5, 6] . Voilà comment ça fonctionne.

Immunité


L'immunité d'une certaine entité peut être décrite comme le fait qu'elle ne change pas dans le temps, ou comme l'impossibilité de changer cette entité.

S'ils essaient de changer un objet immuable, cela ne réussira pas. Au lieu de cela, vous devrez créer un nouvel objet contenant les nouvelles valeurs.

Par exemple, JavaScript utilise souvent la boucle for . Au cours de son travail, comme indiqué ci-dessous, des variables mutables sont utilisées:

 var values = [1, 2, 3, 4, 5]; var sumOfValues = 0; for (var i = 0; i < values.length; i++) { sumOfValues += values[i]; } sumOfValues // 15 

À chaque itération de la boucle, la valeur de la variable i et la valeur de la variable globale (on peut considérer l'état du programme) sumOfValues . Comment dans une telle situation maintenir l'immuabilité des entités? La réponse réside dans l'utilisation de la récursivité.

 let list = [1, 2, 3, 4, 5]; let accumulator = 0; function sum(list, accumulator) { if (list.length == 0) {   return accumulator; } return sum(list.slice(1), accumulator + list[0]); } sum(list, accumulator); // 15 list; // [1, 2, 3, 4, 5] accumulator; // 0 

Il existe une fonction sum() , qui prend un tableau de nombres. Cette fonction s'appelle jusqu'à ce que le tableau soit vide (c'est le cas de base de notre algorithme récursif ). À chacune de ces "itérations", nous ajoutons la valeur d'un des éléments du tableau au paramètre de la fonction accumulator , sans affecter l' accumulator variable globale. Dans ce cas, la list variables globales et l' accumulator restent inchangés; les mêmes valeurs y sont stockées avant et après l'appel de fonction.

Il convient de noter que pour implémenter un tel algorithme, vous pouvez utiliser la méthode de reduce tableau. Nous en parlerons ci-dessous.

En programmation, la tâche est répandue lorsqu'il est nécessaire, sur la base d'un certain modèle d'objet, de créer sa représentation finale. Imaginez que nous ayons une chaîne qui doit être convertie en une vue adaptée à une utilisation dans le cadre de l'URL menant à une certaine ressource.

Si nous résolvons ce problème en utilisant Ruby et en utilisant les principes de la POO, nous allons d'abord créer une classe, disons, en l'appelant UrlSlugify , puis créer une méthode pour cette classe slugify! qui est utilisé pour convertir la chaîne.

 class UrlSlugify attr_reader :text def initialize(text)   @text = text end def slugify!   text.downcase!   text.strip!   text.gsub!(' ', '-') end end UrlSlugify.new(' I will be a url slug   ').slugify! # "i-will-be-a-url-slug" 

Nous avons implémenté l'algorithme, et c'est merveilleux. Nous voyons ici une approche impérative de la programmation, lorsque nous, en traitant la ligne, peignons chaque étape de sa transformation. À savoir, nous réduisons d'abord ses caractères en minuscules, puis supprimons les espaces inutiles, et enfin changeons les espaces restants sur le tiret.

Cependant, lors de cette transformation, une mutation de l'état du programme se produit.

Vous pouvez faire face au problème de mutation en composant des fonctions ou en enchaînant des appels de fonction. En d'autres termes, le résultat renvoyé par la fonction sera utilisé comme entrée pour la fonction suivante, et donc pour toutes les fonctions d'une chaîne. Dans ce cas, la chaîne d'origine ne changera pas.

 let string = " I will be a url slug   "; function slugify(string) { return string.toLowerCase()   .trim()   .split(" ")   .join("-"); } slugify(string); // i-will-be-a-url-slug 

Ici, nous utilisons les fonctions suivantes, représentées en JavaScript par des méthodes de chaîne et de tableau standard:

  • toLowerCase : convertit les caractères de chaîne en toLowerCase .
  • trim : supprime les espaces du début et de la fin d'une ligne.
  • split : divise une chaîne en parties, plaçant les mots séparés par des espaces dans un tableau.
  • join : forme une chaîne de mots séparés par un tiret basé sur un tableau de mots.

Ces quatre fonctions vous permettent de créer une fonction pour convertir une chaîne qui ne change pas cette chaîne elle-même.

Transparence des liens


Créez une fonction square() qui renvoie le résultat de la multiplication d'un nombre par le même nombre:

 function square(n) { return n * n; } 

Il s'agit d'une fonction pure qui renvoie toujours, pour la même valeur d'entrée, la même valeur de sortie.

 square(2); // 4 square(2); // 4 square(2); // 4 // ... 

Par exemple, quel que soit le nombre de numéros 2 sont transmis, cette fonction renvoie toujours le numéro 4 . En conséquence, il s'avère qu'un appel de la forme square(2) peut être remplacé par le numéro 4 . Cela signifie que notre fonction a la propriété de la transparence référentielle.

En général, nous pouvons dire que si une fonction renvoie invariablement le même résultat pour les mêmes valeurs d'entrée qui lui sont transmises, elle a une transparence référentielle.

Functions Fonctions pures + données immuables = transparence référentielle


En utilisant l'idée avancée dans le titre de cette section, vous pouvez mémoriser des fonctions. Supposons que nous ayons une fonction comme celle-ci:

 function sum(a, b) { return a + b; } 

Nous l'appelons comme ceci:

 sum(3, sum(5, 8)); 

La sum(5, 8) appels sum(5, 8) donne toujours 13 . Par conséquent, l'appel ci-dessus peut être réécrit comme suit:

 sum(3, 13); 

Cette expression, à son tour, donne toujours 16 . En conséquence, il peut être remplacé par une constante numérique et mémorisé .

Fonctionne comme des objets de première classe


L'idée de percevoir les fonctions comme des objets de première classe est que ces fonctions peuvent être considérées comme des valeurs et fonctionner avec elles comme des données. Les caractéristiques suivantes des fonctions peuvent être distinguées:

  • Les références aux fonctions peuvent être stockées dans des constantes et des variables et, à travers elles, accéder aux fonctions.
  • Les fonctions peuvent être transmises à d'autres fonctions en tant que paramètres.
  • Les fonctions peuvent être renvoyées par d'autres fonctions.

Autrement dit, il s'agit de considérer les fonctions comme des valeurs et de les traiter comme des données. Avec cette approche, vous pouvez combiner différentes fonctions dans le processus de création de nouvelles fonctions qui implémentent de nouvelles fonctionnalités.

Imaginez que nous ayons une fonction qui ajoute deux valeurs numériques qui lui sont transmises, puis les multiplie par 2 et retourne ce qu'il s'est avéré:

 function doubleSum(a, b) { return (a + b) * 2; } 

Maintenant, nous écrivons une fonction qui soustrait la seconde de la première valeur numérique qui lui est passée, multiplie ce qui s'est passé par 2 et renvoie la valeur calculée:

 function doubleSubtraction(a, b) { return (a - b) * 2; } 

Ces fonctions ont une logique similaire, elles ne diffèrent que par le type d'opérations qu'elles effectuent avec les nombres qui leur sont transmis. Si nous pouvons considérer les fonctions comme des valeurs et les transmettre comme arguments à d'autres fonctions, cela signifie que nous pouvons créer une fonction qui accepte et utilise une autre fonction qui décrit les caractéristiques des calculs. Ces considérations nous permettent d'atteindre les constructions suivantes:

 function sum(a, b) { return a + b; } function subtraction(a, b) { return a - b; } function doubleOperator(f, a, b) { return f(a, b) * 2; } doubleOperator(sum, 3, 1); // 8 doubleOperator(subtraction, 3, 1); // 4 

Comme vous pouvez le voir, la fonction doubleOperator() a maintenant un paramètre f , et la fonction qu'elle représente est utilisée pour traiter les paramètres a et b . En fait, les fonctions sum() et substraction() transmises à la fonction doubleOperator() vous permettent de contrôler le comportement de la fonction doubleOperator() , en le modifiant conformément à la logique implémentée.

Fonctions d'ordre supérieur


Parlant de fonctions d'ordre supérieur, nous entendons des fonctions caractérisées par au moins l'une des caractéristiques suivantes:

  • Une fonction prend une autre fonction comme argument (il peut y avoir plusieurs de ces fonctions).
  • La fonction renvoie une autre fonction à la suite de son travail.

Vous connaissez peut-être déjà les méthodes de tableau JS standard filter() , map() et reduce() . Parlons-en.

▍ Filtrer les tableaux et la méthode filter ()


Supposons que nous ayons une certaine collection d'éléments que nous voulons filtrer par un attribut des éléments de cette collection et former une nouvelle collection. La fonction filter() s'attend à recevoir un critère d'évaluation des éléments, sur la base duquel elle détermine s'il faut ou non inclure un élément dans la collection résultante. Ce critère est défini par la fonction qui lui est transmise, qui renvoie true si la fonction filter() doit inclure un élément dans la collection finale, sinon elle retourne false .

Imaginez que nous avons un tableau d'entiers et que nous voulons le filtrer en obtenant un nouveau tableau qui ne contient que des nombres pairs du tableau d'origine.

Approche impérative


Lors de l'application d'une approche impérative pour résoudre ce problème à l'aide de JavaScript, nous devons implémenter la séquence d'actions suivante:

  • Créez un tableau vide pour les nouveaux éléments (appelons-le evenNumbers ).
  • Itérer sur le tableau d'origine d'entiers (appelons cela des numbers ).
  • Mettez les nombres pairs trouvés dans le tableau des numbers evenNumbers tableau des numbers evenNumbers .

Voici à quoi ressemble l'implémentation de cet algorithme:

 var numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; var evenNumbers = []; for (var i = 0; i < numbers.length; i++) { if (numbers[i] % 2 == 0) {   evenNumbers.push(numbers[i]); } } console.log(evenNumbers); // (6) [0, 2, 4, 6, 8, 10] 

De plus, nous pouvons écrire une fonction (appelons-la even() ) qui, si le nombre est pair, renvoie true , et si elle est impaire, retourne false , puis la transmet à la méthode tableau filter() qui, en vérifiant avec elle, chaque élément du tableau , formera un nouveau tableau contenant uniquement des nombres pairs:

 function even(number) { return number % 2 == 0; } let listOfNumbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; listOfNumbers.filter(even); // [0, 2, 4, 6, 8, 10] 

Voici, en passant, la solution à un problème intéressant concernant le filtrage de tableaux , que j'ai terminé en travaillant sur des tâches de programmation fonctionnelle sur Hacker Rank . À la condition du problème, il était nécessaire de filtrer un tableau d'entiers, affichant uniquement les éléments inférieurs à une valeur donnée de x .

Une solution impérative à ce problème en JavaScript pourrait ressembler à ceci:

 var filterArray = function(x, coll) { var resultArray = []; for (var i = 0; i < coll.length; i++) {   if (coll[i] < x) {     resultArray.push(coll[i]);   } } return resultArray; } console.log(filterArray(3, [10, 9, 8, 2, 7, 5, 1, 3, 0])); // (3) [2, 1, 0] 

L'essence de l'approche impérative est que nous décrivions la séquence d'actions exécutées par la fonction. À savoir, nous décrivons la recherche du tableau, en comparant l'élément actuel du tableau avec x et en plaçant cet élément dans le tableau resultArray s'il réussit le test.

Approche déclarative


Comment passer à une approche déclarative pour résoudre ce problème et l'utilisation correspondante de la méthode filter() , qui est une fonction d'ordre supérieur? Par exemple, cela pourrait ressembler à ceci:

 function smaller(number) { return number < this; } function filterArray(x, listOfNumbers) { return listOfNumbers.filter(smaller, x); } let numbers = [10, 9, 8, 2, 7, 5, 1, 3, 0]; filterArray(3, numbers); // [2, 1, 0] 

Vous trouverez peut-être inhabituel d'utiliser le this dans la fonction smaller() de cet exemple, mais il n'y a rien de compliqué ici. Le this - this est le deuxième argument de la méthode filter() . Dans notre exemple, il s'agit du nombre 3 représenté par le paramètre x de filterArray() . Ce numéro est indiqué par this .

La même approche peut être utilisée si le tableau contient des entités qui ont une structure assez complexe, par exemple des objets. Supposons que nous ayons un tableau stockant des objets contenant les noms des personnes représentées par la propriété name et des informations sur l'âge de ces personnes représentées par la propriété age . Voici à quoi ressemble un tableau:

 let people = [ { name: "TK", age: 26 }, { name: "Kaio", age: 10 }, { name: "Kazumi", age: 30 } ]; 

Nous voulons filtrer ce tableau en sélectionnant uniquement les objets qui sont des personnes dont l'âge a dépassé 21 ans. Voici comment résoudre ce problème:

 function olderThan21(person) { return person.age > 21; } function overAge(people) { return people.filter(olderThan21); } overAge(people); // [{ name: 'TK', age: 26 }, { name: 'Kazumi', age: 30 }] 

Ici, nous avons un tableau avec des objets représentant des personnes. Nous vérifions les éléments de ce tableau à l'aide de la fonction olderThan21() . Dans ce cas, lors de la vérification, nous nous référons à la propriété age de chaque élément, vérifiant si la valeur de cette propriété dépasse 21 . Nous passons cette fonction à la méthode filter() , qui filtre le tableau.

▍ Traitement des éléments du tableau et de la méthode map ()


La méthode map() est utilisée pour convertir les éléments du tableau. Il applique la fonction passée à chaque élément du tableau, puis crée un nouveau tableau composé des éléments modifiés.

Continuons les expériences avec le tableau de people que vous connaissez déjà. Maintenant, nous n'allons pas filtrer ce tableau en fonction de la propriété des objets d' age . Nous devons créer sur sa base une liste de lignes de la forme TK is 26 years old . Dans cette approche, les lignes dans lesquelles les éléments se transforment seront construites selon le modèle p.name is p.age years old , où p.name et p.age sont les valeurs des propriétés correspondantes des éléments du tableau de people .

Une approche impérative pour résoudre ce problème en JavaScript ressemble à ceci:

 var people = [ { name: "TK", age: 26 }, { name: "Kaio", age: 10 }, { name: "Kazumi", age: 30 } ]; var peopleSentences = []; for (var i = 0; i < people.length; i++) { var sentence = people[i].name + " is " + people[i].age + " years old"; peopleSentences.push(sentence); } console.log(peopleSentences); // ['TK is 26 years old', 'Kaio is 10 years old', 'Kazumi is 30 years old'] 

Si vous recourez à une approche déclarative, vous obtenez ce qui suit:

 function makeSentence(person) { return `${person.name} is ${person.age} years old`; } function peopleSentences(people) { return people.map(makeSentence); } peopleSentences(people); // ['TK is 26 years old', 'Kaio is 10 years old', 'Kazumi is 30 years old'] 

En fait, l'idée principale ici est que vous devez faire quelque chose avec chaque élément du tableau d'origine, puis le placer dans un nouveau tableau.

Voici une autre tâche avec le Hacker Rank, qui est dédiée à la mise à jour de la liste . À savoir, nous parlons de changer les valeurs des éléments d'un tableau numérique existant en leurs valeurs absolues. Ainsi, par exemple, lors du traitement d'un tableau [1, 2, 3, -4, 5] il prendra la forme [1, 2, 3, 4, 5] car la valeur absolue de -4 est 4 .

Voici un exemple de solution simple à ce problème, lorsque nous parcourons un tableau et modifions les valeurs de ses éléments en leurs valeurs absolues.

 var values = [1, 2, 3, -4, 5]; for (var i = 0; i < values.length; i++) { values[i] = Math.abs(values[i]); } console.log(values); // [1, 2, 3, 4, 5] 

Ici, pour convertir les valeurs des éléments du tableau, la méthode Math.abs() est utilisée, les éléments modifiés sont écrits au même endroit où ils étaient avant la conversion.

.

, , , . . , , , .

, , map() . ?

, abs() , , .

 Math.abs(-1); // 1 Math.abs(1); // 1 Math.abs(-2); // 2 Math.abs(2); // 2 

, , .

, , Math.abs() map() . , ? map() . :

 let values = [1, 2, 3, -4, 5]; function updateListMap(values) { return values.map(Math.abs); } updateListMap(values); // [1, 2, 3, 4, 5] 

, , , , , .

▍ reduce()


reduce() .

. , -. Product 1 , Product 2 , Product 3 Product 4 . .

, . Par exemple, cela pourrait ressembler à ceci:

 var orders = [ { productTitle: "Product 1", amount: 10 }, { productTitle: "Product 2", amount: 30 }, { productTitle: "Product 3", amount: 20 }, { productTitle: "Product 4", amount: 60 } ]; var totalAmount = 0; for (var i = 0; i < orders.length; i++) { totalAmount += orders[i].amount; } console.log(totalAmount); // 120 

reduce() , ( sumAmount() ), , reduce() :

 let shoppingCart = [ { productTitle: "Product 1", amount: 10 }, { productTitle: "Product 2", amount: 30 }, { productTitle: "Product 3", amount: 20 }, { productTitle: "Product 4", amount: 60 } ]; const sumAmount = (currentTotalAmount, order) => currentTotalAmount + order.amount; function getTotalAmount(shoppingCart) { return shoppingCart.reduce(sumAmount, 0); } getTotalAmount(shoppingCart); // 120 

shoppingCart , , sumAmount() , ( order , amount ), — currentTotalAmount .

reduce() , getTotalAmount() , sumAmount() , 0 .

map() reduce() . «»? , map() shoppingCart , amount , reduce() sumAmount() . :

 const getAmount = (order) => order.amount; const sumAmount = (acc, amount) => acc + amount; function getTotalAmount(shoppingCart) { return shoppingCart   .map(getAmount)   .reduce(sumAmount, 0); } getTotalAmount(shoppingCart); // 120 

getAmount() amount . map() , , , [10, 30, 20, 60] . , reduce() , .

▍ filter(), map() reduce()


, , filter() , map() reduce() . , , .

-. , :

 let shoppingCart = [ { productTitle: "Functional Programming", type: "books", amount: 10 }, { productTitle: "Kindle", type: "eletronics", amount: 30 }, { productTitle: "Shoes", type: "fashion", amount: 20 }, { productTitle: "Clean Code", type: "books", amount: 60 } ] 

. :

  • type , , books .
  • , .
  • .

, :

 let shoppingCart = [ { productTitle: "Functional Programming", type: "books", amount: 10 }, { productTitle: "Kindle", type: "eletronics", amount: 30 }, { productTitle: "Shoes", type: "fashion", amount: 20 }, { productTitle: "Clean Code", type: "books", amount: 60 } ] const byBooks = (order) => order.type == "books"; const getAmount = (order) => order.amount; const sumAmount = (acc, amount) => acc + amount; function getTotalAmount(shoppingCart) { return shoppingCart   .filter(byBooks)   .map(getAmount)   .reduce(sumAmount, 0); } getTotalAmount(shoppingCart); // 70 

Résumé


JavaScript-. , .

Chers lecteurs! ?



Source: https://habr.com/ru/post/fr434112/


All Articles