Programmation fonctionnelle avec des tableaux d'objets JavaScript
Nous considérons l'utilisation de map
filter
et reduce
pour manipuler des tableaux d'objets, en utilisant des techniques empruntées à la programmation fonctionnelle.
une tâche commune dans n'importe quelle application JavaScript. Heureusement, de nouveaux opérateurs de gestion de tableaux map
filter
et reduce
sont largement supportés. Bien que la documentation de ces fonctionnalités soit suffisante, elle montre souvent des cas d'utilisation très simples pour la mise en œuvre. Dans l'utilisation quotidienne, nous avons souvent besoin d'utiliser ces méthodes pour traiter des tableaux d'objets de données, ce qui est le scénario qui manque à la documentation. De plus, ces opérateurs sont souvent vus dans des langages fonctionnels et apportent à JavaScript une nouvelle perspective d'itération à travers des objets fonctionnels.
Dans cet article, nous allons décomposer quelques exemples d'utilisation de la carte
réduire
et filtrer
pour manipuler des tableaux d'objets. Grâce à ces exemples, nous apprendrons à quel point les méthodes sont puissantes, tout en comprenant leur relation avec la programmation fonctionnelle.
Programmation fonctionnelle: les bonnes parties
La programmation fonctionnelle a de nombreux concepts qui vont au-delà de ce que nous sommes. essayant d'accomplir. Pour le contexte de cet article, nous allons discuter de certaines des bases absolues.
Tout au long des exemples, nous allons favoriser les expressions simples sur plusieurs instructions. Cela signifie que nous allons réduire considérablement le nombre de variables utilisées et utiliser à la place la composition de la fonction et le chaînage des méthodes chaque fois que cela est possible. Ce style de programmation réduit le besoin de maintenir l'état, ce qui correspond aux pratiques de programmation fonctionnelle.
Un avantage commun de l'utilisation de map
et filter
est que chaque opérateur crée un nouveau tableau. En créant de nouveaux tableaux au lieu de modifier ou de muter des données existantes, notre code sera plus facile à raisonner, puisque la mutation des données peut provoquer des effets secondaires inattendus.
Enfin, nous tirerons profit du fait que chaque opérateur est plus haut. fonction de commande. Autrement dit, chaque opérateur accepte une fonction de rappel en tant que paramètre. La fonction de rappel nous permet de personnaliser le comportement grâce à la délégation de fonction, un outil puissant pour la flexibilité et la lisibilité du code.
L'idée ici n'est pas d'être «entièrement fonctionnel» mais de tirer parti des concepts. Si ces concepts vous concernent, une feuille de triche contenant une liste de méthodes / fonctions JavaScript fréquemment utilisées dans la programmation fonctionnelle est disponible en téléchargement. Le Functional Programming Cheat Sheet est une excellente référence rapide à garder à portée de main.
Télécharger la feuille de triche maintenant
Arrows Everywhere / Function Expressions
En venant des anciennes bibliothèques JavaScript la "grosse flèche" peut sembler un peu étranger. Cet opérateur de flèche =>
est utilisé pour réduire la quantité de code nécessaire pour écrire une fonction. Au lieu d'écrire explicitement la fonction () avec une instruction return quand nous avons besoin d'une fonction simple, nous pouvons utiliser Identifier => Expression
. Cela aide à rendre notre code plus facile à lire, en particulier lorsque vous utilisez des fonctions d'ordre supérieur, des méthodes qui prennent une fonction en paramètre. .map
.reduce
et .filter
sont toutes des fonctions d'ordre supérieur
Sans expressions de fonction, nous pourrions écrire quelque chose comme ceci:
Laisser cart = [
{ name: "Drink", price: 3.12 },
{ name: "Steak", price: 45.15},
{ name: "Drink", price: 11.01}
];
let steakOrders = cart.filter (fonction (obj) {
return obj.name === "Steak"
});
// {name: "Steak", prix: 45.15}
Avec l'opérateur flèche, on pourrait écrire le même code en omettant la fonction comme dans l'exemple ci-dessous:
let steakOrders = cart.filter ((obj) = > {return obj.name === "Steak"});
// {name: "Steak", prix: 45.15}
Cependant, nous pouvons aller plus loin car la plus grande partie de l'expression est déjà implicite quand on utilise un opérateur fléché. Le code peut encore être réduit:
let steakOrders = cart.filter (obj => obj.name === "Steak");
// {name: "Steak", prix: 45.15}
Dans cet exemple, nous pouvons voir que la flèche a aidé à définir une fonction concise dans l'instruction filter. Maintenant que nous avons vu comment la flèche peut améliorer la façon dont les expressions sont écrites, apprenons plus sur l'utilisation de la méthode filter
Filtering Object Arrays
La méthode filter
crée un nouveau tableau avec tous les éléments qui réussissent le test implémenté par la fonction fournie. Lorsque vous utilisez la méthode de filtrage sur des objets, nous pouvons créer un ensemble restreint basé sur une ou plusieurs propriétés de l'objet.
Dans cet exemple, nous allons obtenir les objets de la collection avec le nom "Steak" en transmettant une fonction expression qui teste la valeur de la propriété name, obj => obj.name === "Steak"
:
let cart = [
{ name: "Drink", price: 3.12 },
{ name: "Steak", price: 45.15},
{ name: "Drink", price: 11.01}
];
Laissez steakOrders = cart.filter (obj => obj.name === "Steak");
// {name: "Steak", prix: 45.15}
Considérons que nous avons besoin d'un sous-ensemble de données où nous devons filtrer sur plusieurs valeurs de propriétés. Nous pouvons accomplir cela en écrivant une fonction qui contient plusieurs tests:
let expensiveDrinkOrders =
cart.filter (x => x.name === "Drink" && x.price> 10);
// {name: "Drink", prix: 11.01}
Lors du filtrage sur plusieurs valeurs, l'expression de la fonction peut devenir longue ce qui rend le code difficile à raisonner en un coup d'oeil. En utilisant quelques techniques, nous pouvons simplifier le code et le rendre plus gérable.
Une façon d'aborder le problème consiste à utiliser plusieurs filtres à la place de l'opérateur &&
. En enchaînant plusieurs filtres, nous pouvons rendre la déclaration plus facile à raisonner tout en produisant les mêmes résultats:
let expensiveDrinkOrders = cart.filter (x => x.name === "Drink")
.filter (x => x.prix> 10);
// {name: "Boire", prix: 11.01}
Une autre façon d'exprimer un filtre complexe consiste à créer une fonction nommée. Cela nous permettra d'écrire le filtre d'une manière "lisible par l'homme":
const drinksGreaterThan10 =
obj => obj.name === "Buvez" && obj.price> 10;
let result = cart.filter (drinksGreaterThan10);
Bien que cela fonctionne comme prévu, la valeur du prix est codée en dur. Nous pouvons optimiser la lisibilité et la flexibilité en permettant de fixer le prix à l'exécution en utilisant un paramètre
Au début, il peut sembler intuitif d'ajouter le paramètre avec obj
de telle sorte qu'il se lise (obj, coût)
où coût
est le prix variable:
const drinksGreaterThan =
(obj, coût) => obj.name === "Drink" && obj.price> cost;
Laisser result = cart.filter (drinksGreaterThan (10)); // Erreur
Currying en JavaScript
Malheureusement, cela créerait une erreur car le filtre attend une fonction avec un seul paramètre et maintenant nous essayons d'en utiliser deux. Pour satisfaire les paramètres correctement, nous aurions besoin d'écrire l'instruction de filtre en tant que .filter (x => drinksGreaterThan (x, 10))
.
Pour fournir une meilleure solution, nous pouvons utiliser une programmation fonctionnelle technique appelée currying. Currying permet de traduire une fonction avec plusieurs arguments en une séquence de fonctions, ce qui nous permet de créer une parité avec d'autres signatures de fonctions
Déplaçons le paramètre cost vers une expression de fonction et réécrivons la fonction drinksGreaterThan
en utilisant la curry. C'est juste une utilisation intelligente d'une fonction d'ordre supérieur, où drinksGreatThan
devient une fonction qui accepte un coût et retourne une autre fonction qui accepte un obj
:
const drinksGreaterThan =
cost => obj => obj.name === "Buvez" && obj.price> coût;
Laisser result = cart.filter (drinksGreaterThan (10));
// {name: "Drink", prix: 11.01}
La fonction complete nous donne un filtre nommé avec une lisibilité et une réutilisabilité maximales.
Mapping Objects
La méthode map est un outil essentiel pour convertir et projeter des données. La méthode map
crée un nouveau tableau avec les résultats de l'appel d'une fonction fournie sur chaque élément du tableau appelant.
Dans un scénario où nous consommons une source de données externe, nous pouvons recevoir plus de données que nécessaire. Au lieu de traiter l'ensemble de données complet, nous pouvons utiliser la carte
pour projeter les données comme bon nous semble. Dans l'exemple suivant, nous recevrons des données comprenant plusieurs propriétés, dont la plupart ne sont pas utilisées dans notre vue actuelle.
L'objet initial contient: id
name
, prix
coût
et taille
:
let jsonData = [
{ id: 1, name: "Soda", price: 3.12, cost: 1.04, size: "4oz", },
{ id: 2, name: "Beer", price: 6.50, cost: 2.45, size: "8oz" },
{ id: 3, name: "Margarita", price: 12.99, cost: 4.45, size: "12oz" }
];
Pour notre vue, nous n'utiliserons que le propriétés de nom et de prix, donc nous allons utiliser la carte pour construire un nouvel ensemble de données:
laissez drinkMenu = jsonData.map (x => ({nom: x.nom, prix: x.prix}));
// [{"name":"Soda","price":3.12}, {"name":"Beer","price":6.5}, {"name":"Margarita","price":12.99}]
En utilisant la carte
nous pouvons assigner des valeurs de chaque objet à un nouvel objet qui n'a que le nom et le prix.
Si la map
expression de la fonction, nous pouvons assigner la fonction à une variable. Cette abstraction ne change pas la façon dont fonctionne la carte
mais l'intention du code devient plus claire pour ceux qui ne connaissent peut-être pas le domaine.
En créant une méthode unique nommée toMenuItem
nous pouvons immédiatement donner un contexte à notre code. L'instruction map devient auto-documentante car elle peut être lue à haute voix "données JSON, mapper à l'élément de menu".
const toMenuItem = x => ({nom: x.nom, prix: x.prix});
laissez drinkMenu = jsonData.map (toMenuItem);
// [{"name":"Soda","price":3.12}, {"name":"Beer","price":6.5}, {"name":"Margarita","price":12.99}]
En continuant avec cet exemple, nous utiliserons à nouveau la map
pour appliquer une conversion de valeur. Supposons que nous voulons convertir les prix des menus existants de USD à Euro. Puisque map ne mute pas le tableau initial, nous n'avons pas besoin de nous inquiéter de l'état de l'instance drinkMenu
en raison de notre conversion.
Utilisons une fonction similaire pour convertir les valeurs de prix, sauf cette fois, nous garderons toutes les propriétés disponibles pour l'objet. Pour nous assurer que nous copions chaque valeur, nous utiliserons le ...
opérateur spread:
const drinkMenuEuro = drinkMenu.map (x => ({... x, prix: (x.prix * 0.81) .toFixe (2)})
// [{"name":"Soda","price":"2.53"},{"name":"Beer","price":"5.27"},{"name":"Margarita","price":"10.52"}]
Dans cet exemple, ... x
(l'opérateur spread) fait beaucoup pour nous. Ce petit morceau de code assure que nous copions l'objet complet et ses propriétés en ne modifiant que la valeur du prix. L'avantage de l'utilisation de l'opérateur de propagation est que nous pouvons ajouter des propriétés supplémentaires plus tard à drinkMenu
et ils seront inclus drinkMenuEuro
automatiquement
Réduire avec des objets
Réduire est un sous-utilisé et parfois un opérateur de tableau mal compris. La méthode réduire
applique une fonction à un accumulateur et à chaque élément du tableau (de gauche à droite) pour le réduire à une valeur unique.
Lors de l'utilisation de réduire avec un tableau d'objets, la valeur l'objet résultant de l'itération précédente. Si nous devons obtenir la valeur totale d'une propriété d'objets (c'est-à-dire obj.price
), nous devons d'abord mapper cette propriété sur une seule valeur:
let cart = [
{ name: "Soda", price: 3.12 },
{ name: "Margarita", price: 12.99},
{ name: "Beer", price: 6.50}
];
let totalPrice = cart.reduce ((acc, next) => prix d'acc. + prix suivant);
// NaN parce que acc ne peut pas être à la fois un objet et un nombre
Laisser totalPrice = cart.map (obj => obj.price) .reduce ((acc, next) => acc + next);
// 22.61
Dans l'exemple précédent, nous avons vu que lorsque nous réduisons un tableau d'objets, alors l'accumulateur est un objet. La méthode réduire
est extrêmement utile sur des objets dans de bonnes conditions. Au lieu d'essayer de faire la somme du prix d'un objet, utilisons reduce pour trouver l'objet avec le plus gros prix:
let mostExpensiveItem = cart.reduce ((acc, next) => prix.acc.prix> suivant: acc: prochain);
// {name: "Margarita", prix: 12.99}
Ici, nous utilisons la fonctionnalité de réduire pour nous donner l'objet le plus grand.
Nous pouvons utiliser un délégué de fonction pour décrire notre intention. Cela aidera à clarifier l'expression à l'intérieur de l'opérateur de réduction. Notre dernière fonction lit "réduire par le plus grand prix":
let byGreatestPrice = (article, suivant) => article.prix> prix suivant? item: suivant;
let mostExpensiveItem = cart.reduce (parGreatestPrice);
// {name: "Margarita", prix: 12.99}
Conclusion
Dans cet article nous avons regardé l'utilisation de carte
réduire
et filtre
pour manipuler des tableaux d'objets. Puisque ces opérateurs de tableau ne modifient pas l'état du tableau appelant, nous pouvons les utiliser efficacement sans avoir à craindre les effets secondaires. En utilisant des techniques empruntées à la programmation fonctionnelle, nous pouvons écrire de puissants opérateurs de tableaux expressifs. Grâce à la délégation de fonction, nous avons la possibilité de créer des noms de fonctions explicites pour créer du code lisible.
Si les exemples montrant carte
réduisent
et filtre
ont piqué votre intérêt pour la programmation fonctionnelle, puis jetez un coup d'œil à la feuille de triche de programmation fonctionnelle pour plus d'exemples d'utilisation d'une approche fonctionnelle pour créer des applications JavaScript.
Découvrez la feuille de triche
Contenu connexe:
Les commentaires sont désactivés en mode prévisualisation.
[ad_2]
Source link