Comment utiliser la carte, le filtrage et la réduction en JavaScript


La programmation fonctionnelle a fait sensation dans le
monde du développement ces jours-ci. Et pour cause: les techniques fonctionnelles
peut vous aider à écrire un code déclaratif plus facile à comprendre
en un coup d’œil, refactoriser et tester.

L’une des pierres angulaires de
la programmation fonctionnelle est son utilisation spéciale des listes et des opérations de liste.
Et ces choses sont exactement ce que le son est comme elles: des tableaux de
choses, et ce que vous leur faites. Mais l’état d’esprit fonctionnel traite
eux un peu différemment de ce à quoi vous pourriez vous attendre.

Cet article examinera de près ce que j’aime appeler les «trois grandes» opérations de liste: map, filter, et reduce.
S’entourer de ces trois fonctions est une étape importante
pour pouvoir écrire un code fonctionnel propre et ouvrir les portes
aux techniques extrêmement puissantes de fonctionnelles et réactives
programmation.

Curieuse? Plongeons-nous.

Une carte de liste en liste

Souvent,
nous devons prendre un tableau et modifier chaque élément de
exactement de la même manière. Des exemples typiques de ceci sont la quadrature de chaque
élément dans un tableau de nombres, récupérant le nom d’une liste de
utilisateurs, ou exécuter une expression régulière sur un tableau de chaînes.

map est une méthode conçue pour faire exactement cela. Il est défini sur Array.prototype, vous pouvez donc l’appeler sur n’importe quel tableau, et il accepte un rappel comme premier argument.

La syntaxe de la carte est la suivante.

Quand vous appelez map sur un tableau, il exécute ce rappel sur chaque élément qu’il contient, renvoyant un Nouveau tableau avec toutes les valeurs renvoyées par le rappel.

Sous la capuche, map passe trois arguments à votre rappel:

  1. le article actuel dans le tableau
  2. le index du tableau de l’élément actuel
  3. le tableau entier vous avez appelé map sur

Regardons un peu de code.

map en pratique

Supposons que nous ayons une application qui gère un éventail de vos tâches pour la journée. Chaque task est un objet, chacun avec un name et duration propriété:

Disons que nous voulons créer un nouveau tableau avec juste le nom de
chaque tâche, afin que nous puissions jeter un coup d’œil à tout ce que nous avons accompli aujourd’hui.
Utilisant un for boucle, nous écririons quelque chose comme ceci:

JavaScript offre également un forEach boucle. Il fonctionne comme un for loop, mais gère tout le désordre de la vérification de notre index de boucle par rapport à la longueur du tableau pour nous:

En utilisant map, on peut simplement écrire:

Ici j'ai inclus le index et array
paramètres pour vous rappeler qu'ils sont là si vous en avez besoin. Depuis que je
ne les a pas utilisés ici, cependant, vous pouvez les laisser de côté, et le code
fonctionnerait très bien.

Une façon d'écrire encore plus succincte map dans JavaScript moderne est avec des fonctions fléchées.

Les fonctions fléchées sont une forme abrégée des fonctions sur une ligne qui ont juste un return déclaration. Cela ne devient pas beaucoup plus lisible que cela.

Il existe quelques différences importantes entre les différentes approches:

  1. En utilisant map, vous n'avez pas à gérer l'état du for bouclez-vous.
  2. Avec map vous pouvez agir directement sur l'élément, plutôt que d'avoir à indexer dans le tableau.
  3. Vous n'êtes pas obligé de créer un nouveau tableau et push dans ça. map renvoie le produit fini en une seule fois, nous pouvons donc simplement affecter la valeur de retour à une nouvelle variable.
  4. Tu faire ne pas oublier d'inclure un return déclaration dans votre rappel. Si vous ne le faites pas, vous obtiendrez un nouveau tableau rempli de undefined.

Il s'avère que, tout des fonctions que nous allons examiner aujourd'hui partagent ces caractéristiques.

le
le fait que nous n'ayons pas à gérer manuellement l'état de la boucle rend
notre code plus simple et plus maintenable. Le fait que nous pouvons opérer
directement sur l'élément au lieu d'avoir à indexer dans le tableau fait
des choses plus lisibles.

Utilisant un forEach loop résout ces deux problèmes pour nous. Mais map présente encore au moins deux avantages distincts:

  1. forEach Retour undefined, donc il ne s'enchaîne pas avec d'autres méthodes de tableau. map renvoie un tableau, donc vous pouvez chaînez-le avec d'autres méthodes de tableau.
  2. map Retour un tableau avec le produit fini, plutôt que de nous obliger à muter un tableau à l'intérieur de la boucle.

En gardant
le nombre d'endroits où vous modifiez l'état au minimum absolu est
un principe important de la programmation fonctionnelle. Cela rend plus sûr et
code plus intelligible.

Gotchas

Le rappel auquel vous passez map doit avoir un explicite return déclaration, ou map crachera un tableau plein de undefined. Il n'est pas difficile de se rappeler d'inclure un return valeur, mais ce n'est pas difficile à oublier.

Si vous faire oublier, map
ne se plaindra pas. Au lieu de cela, il rendra tranquillement un tableau plein
de rien. Des erreurs silencieuses comme celle-ci peuvent être étonnamment difficiles à déboguer.

Heureusement, c'est le seulement gotcha avec map. Mais c'est un piège assez courant que je suis obligé de souligner: assurez-vous toujours que votre rappel contient un return déclaration!

la mise en oeuvre

La lecture des implémentations est une partie importante de la compréhension. Alors, écrivons notre propre poids léger map pour mieux comprendre ce qui se passe sous le capot. Si vous souhaitez voir une mise en œuvre de qualité de production, consultez Polyfill de Mozilla chez MDN.

Ce code accepte un tableau et une fonction de rappel comme
arguments. Il crée ensuite un nouveau tableau; exécute le rappel sur chaque
élément sur le tableau que nous avons passé; pousse les résultats dans le nouveau
tableau; et renvoie le nouveau tableau. Si vous exécutez ceci dans votre console,
vous obtiendrez le même résultat qu'avant.

Pendant que nous utilisons un for
boucle sous le capot, l'enveloppant dans une fonction cache les détails
et nous permet de travailler avec l'abstraction à la place.

Cela rend notre code plus déclaratif - il dit quoi faire, pas Comment pour le faire. Vous apprécierez combien plus lisible, maintenable et, euh, déboguable cela peut rendre votre code.

Filtrer le bruit

La prochaine de nos opérations de tableau est filter. Il fait exactement ce que cela ressemble: il prend un tableau et filtre les éléments indésirables.

La syntaxe du filtre est:

Juste comme map, filter passe votre rappel trois arguments:

  1. le article actuel
  2. le index actuel
  3. le tableau vous avez appelé filter sur

Prenons l'exemple suivant qui filtre toute chaîne de moins de 8 caractères.

Le résultat attendu sera:

Allons
revisitez notre exemple de tâche. Au lieu de retirer les noms de chacun
tâche, disons que je veux obtenir une liste des tâches qui m'ont pris deux
heures ou plus à faire.

En utilisant forEach, nous écririons:

Avec filter on peut simplement écrire:

Juste comme map, filter nous permet:

  • éviter de muter un tableau à l'intérieur d'un forEach ou for boucle
  • assignez son résultat directement à une nouvelle variable, plutôt que de pousser dans un tableau que nous avons défini ailleurs

Gotchas

Le rappel auquel vous passez map doit inclure une instruction return si vous voulez qu'elle fonctionne correctement. Avec filter, vous devez également inclure une instruction return (sauf si vous utilisez des fonctions fléchées), et vous doit assurez-vous qu'il renvoie une valeur booléenne.

Si vous oubliez votre déclaration de retour, votre rappel reviendra undefined, lequel filter contraindra inutilement à false. Au lieu de lancer une erreur, il renverra silencieusement un tableau vide!

Si vous empruntez l'autre voie et retournez quelque chose qui n'est pas explicitement true ou false, puis filter essaiera de comprendre ce que vous vouliez dire en postulant Règles de coercition de JavaScript. Le plus souvent, c'est un bogue. Et, tout comme si vous oubliez votre déclaration de retour, ce sera silencieux.

Toujours assurez-vous que vos rappels incluent une instruction de retour explicite. Et toujours assurez-vous que vos rappels dans filter revenir true ou false. Votre santé mentale vous remerciera.

la mise en oeuvre

Encore une fois, la meilleure façon de comprendre un morceau de code est ... eh bien, de l'écrire. Roulons notre propre poids léger filter. Les bons gars de Mozilla ont un polyfill de qualité industrielle à vous de lire aussi.

Méthode de réduction

La syntaxe de reduce La méthode de tableau en JavaScript est:

map crée un nouveau tableau en transformant chaque élément dans un tableau, individuellement. filter crée un nouveau tableau en supprimant les éléments qui n'appartiennent pas. reduce, d'autre part, prend tous les éléments d'un tableau, et réduit les en une seule valeur.

Juste comme map et filter, reduce est défini sur Array.prototype
et donc disponible sur n'importe quel tableau, et vous passez un rappel comme premier
argument. Mais il faut aussi un deuxième argument: la valeur pour commencer
combinant tous vos éléments de tableau dans.

reduce passe votre rappel quatre arguments:

  1. le valeur actuelle
  2. le valeur précédente
  3. le index actuel
  4. le tableau vous avez appelé reduce sur

Notez que le rappel obtient un valeur précédente à chaque itération. Lors de la première itération, il y a est aucune valeur précédente. C'est pourquoi vous avez la possibilité de passer reduce une valeur initiale: elle agit comme la "valeur précédente" pour la première itération, alors qu'il n'y en aurait pas autrement.

Enfin, gardez à l'esprit que reduce renvoie une seule valeur, ne pas un tableau contenant un seul élément. C'est plus important qu'il n'y paraît, et j'y reviendrai dans les exemples.

reduce en pratique

Supposons que vous souhaitiez trouver la somme d'une liste de nombres. En utilisant une boucle, cela ressemblerait à ceci:

Bien que ce ne soit pas un mauvais cas d'utilisation pour forEach, reduce a encore l'avantage de nous permettre d'éviter la mutation. Avec reduce, nous écririons:

Premièrement, nous appelons reduce sur notre liste de numbers. nous
lui passer un rappel, qui accepte la valeur précédente et la valeur actuelle
comme arguments, et renvoie le résultat de leur addition. Depuis que nous
passé 0 comme deuxième argument pour reduce, il utilisera cela comme la valeur de previous sur la première itération.

Avec les fonctions fléchées, nous l'écririons comme ceci:

Si nous procédons étape par étape, cela ressemble à ceci:

ItérationprécédentActuelTotal
1011
2123
3336
464dix
5dix515

Si vous n'êtes pas fan des tables, exécutez cet extrait dans la console:

Récapituler: reduce itère sur tout
les éléments d'un tableau, en les combinant comme vous le spécifiez dans votre
rappeler. À chaque itération, votre rappel a accès au précédent valeur, qui est le total-jusqu'ici, ou valeur accumulée; la valeur actuelle; la index actuel; et le tout tableau, si vous en avez besoin.

Revenons à notre exemple de tâches. Nous avons obtenu une liste de noms de tâches de map, et une liste filtrée des tâches qui ont pris du temps avec ... eh bien, filter.

Et si nous voulions connaître le temps total que nous avons passé à travailler aujourd'hui?

Utilisant un forEach boucle, vous écririez:

Avec reduce, cela devient:

C'est presque tout ce qu'il y a à faire. Presque, parce que JavaScript nous fournit une autre méthode peu connue, appelée reduceRight. Dans les exemples ci-dessus, reduce a commencé au première élément dans le tableau, en itérant de gauche à droite:

reduceRight fait la même chose, mais dans le sens inverse:

j'utilise reduce tous les jours, mais je n'ai jamais eu besoin reduceRight. Je pense que vous ne le ferez probablement pas non plus. Mais si jamais vous le faites, vous savez maintenant que c'est là.

Gotchas

Les trois grands pièges avec reduce sont:

  1. Oublier de return
  2. Oublier une valeur initiale
  3. Attente d'un tableau quand reduce renvoie une seule valeur

Heureusement,
les deux premiers sont faciles à éviter. Décider quelle est votre valeur initiale
devrait être dépend de ce que vous faites, mais vous comprendrez
rapidement.

Le dernier peut sembler un peu étrange. Si reduce ne renvoie qu'une seule valeur, pourquoi vous attendriez-vous à un tableau?

Il y a quelques bonnes raisons à cela. Première, reduce renvoie toujours un seul valeur, pas toujours un seul nombre.
Si vous réduisez un tableau de tableaux, par exemple, il renverra un seul
tableau. Si vous avez l'habitude de réduire les tableaux, il serait juste de
s'attendre à ce qu'un tableau contenant un seul élément ne soit pas un
Cas.

Deuxièmement, si reduce fait renvoie un tableau avec une seule valeur, il jouerait naturellement bien avec map et filter, et d'autres fonctions sur les tableaux que vous utiliserez probablement avec lui.

la mise en oeuvre

C'est l'heure de notre dernier regard sous le capot. Comme d'habitude, Mozilla a un polyfill pare-balles pour réduire si vous voulez le vérifier.

Deux choses à noter, ici:

  1. Cette fois, j'ai utilisé le nom accumulator au lieu de previous. C'est ce que vous verrez habituellement dans la nature.
  2. J'assigne accumulator une valeur initiale, si un utilisateur en fournit une, et par défaut 0, si non. C'est ainsi que le réel reduce se comporte également.

Assemblage: mapper, filtrer, réduire et chaîner

À ce stade, vous n'êtes peut-être pas cette impressionné.

C'est suffisant: map, filter, et reduce, à eux seuls, ne sont pas très intéressants.

Après tout, leur véritable pouvoir réside dans leur chaînabilité.

Disons que je veux faire ce qui suit:

  1. Collectez l'équivalent de deux jours de tâches.
  2. Convertissez les durées des tâches en heures au lieu de minutes.
  3. Filtrez tout ce qui a pris deux heures ou plus.
  4. Résumez le tout.
  5. Multipliez le résultat par un taux horaire de facturation.
  6. Sortez un montant en dollars formaté.

Tout d'abord, définissons nos tâches pour lundi et mardi:

Et maintenant, notre belle transformation:

Si vous êtes arrivé jusqu'ici, cela devrait être assez simple. Il y a cependant deux aspects étranges à expliquer.

Tout d'abord, à la ligne 10, je dois écrire:

Deux choses à expliquer ici:

  1. Les signes plus devant accumulator et current contraindre leurs valeurs à des nombres. Si vous ne le faites pas, la valeur de retour sera la chaîne plutôt inutile, "12510075100".
  2. Si ne mettez pas cette somme entre crochets, reduce crachera une seule valeur, ne pas un tableau. Cela finirait par jeter un TypeError, car vous ne pouvez utiliser que map sur un tableau!

Le deuxième élément qui pourrait vous mettre un peu mal à l'aise est le dernier reduce, à savoir:

Cet appel à map renvoie un tableau contenant une seule valeur. Ici, nous appelons reduce pour retirer cette valeur.

Enfin, voyons comment notre ami le forEach boucle le ferait:

Tolérable, mais bruyant.

Conclusion et prochaines étapes

Dans ce didacticiel, vous avez appris comment map, filter, et reduce
travail; comment les utiliser; et à peu près comment ils sont mis en œuvre. Tu as vu
qu'ils vous permettent tous d'éviter la mutation de l'état, qui en utilisant for et forEach loops nécessite, et vous devriez maintenant avoir une bonne idée de la façon de les enchaîner.

Par
maintenant, je suis sûr que vous avez hâte de vous entraîner et de lire davantage. Pour un
masterclass en programmation fonctionnelle en JavaScript, découvrez notre
Cours en ligne.

Questions, commentaires ou confusions? Laissez-les dans la section des commentaires.

Close Menu