Apprenez en lisant dans l'ordre

map et filter — transformer ou filtrer un tableau

map transforme chaque élément, filter ne garde que ceux qui correspondent. Au programme : tableau d'origine intact, chaînage et argument index.

Que tu extraies les prix TTC d'une carte ou que tu ne gardes que les produits en stock, tu finis toujours par réécrire les mêmes lignes : déclarer un tableau vide, puis appeler push dans un forEach.

Cet article présente map, qui transforme les éléments, et filter, qui ne garde que les éléments qui remplissent une condition.

Transformer chaque élément dans un nouveau tableau — map

Imagine que tu veux convertir tous les prix hors taxes de la carte d'un café en prix TTC. Le résultat doit être un tableau dans le même ordre que les 3 articles d'origine, où seuls les prix ont changé. Tu peux calculer chaque prix avec forEach, mais pour rassembler les résultats, tu dois créer toi-même un tableau et le remplir tout au long de la boucle.

La méthode de tableau map (une méthode qui passe chaque élément à un callback et renvoie un nouveau tableau contenant les valeurs de retour, dans l'ordre) se charge à la fois de créer le tableau de résultats et de parcourir les éléments. La fonction que tu lui passes décrit seulement comment transformer un élément, et renvoie le résultat.

const menuItems = [
  { name: "Café filtre", price: 480 },
  { name: "Café latte", price: 540 },
  { name: "Cheesecake", price: 620 },
];

// Seule la transformation d'un élément va dans la fonction
const toTaxIncluded = (item) => Math.floor(item.price * 1.1);

// toTaxIncluded est appelée une fois par élément, et ses valeurs de retour remplissent le nouveau tableau
const taxIncluded = menuItems.map(toTaxIncluded);
console.log(taxIncluded.join(", "));   // 528, 594, 682
console.log(taxIncluded.length);       // 3
console.log(menuItems.length);         // 3
Chaque valeur renvoyée arrive à la même position
Une seule fonctiontoTaxIncludedmenuItemsindex 0 : 480menuItemsindex 1 : 540menuItemsindex 2 : 620480 × 1.1 →renvoie 528540 × 1.1 →renvoie 594620 × 1.1 →renvoie 682taxIncludedindex 0taxIncludedindex 1taxIncludedindex 2
Les 3 valeurs renvoyées entrent dans taxIncluded dans le même ordre que les éléments d'origine. map ne change jamais le nombre d'éléments, seulement leurs valeurs.

map se contente de passer les éléments un par un au callback et d'ajouter au nouveau tableau ce qui revient : le nombre d'éléments ne peut donc pas changer. Pour filtrer un tableau, ce qui change justement ce nombre, il faut une autre méthode.

forEach ne renvoie rien

forEach se contente d'appeler la fonction que tu lui passes ; il ne rassemble pas les valeurs de retour. Il renvoie toujours undefined : même si tu écris const list = menuItems.forEach(toTaxIncluded);, list vaut undefined. Pour récupérer les résultats sous forme de tableau, utilise map.

Tu prépares, pour une boulangerie, la liste des prix soldés et la liste des noms de produits. bakeryItems est déjà déclaré.

① Crée un tableau salePrices avec les prix soldés (price multiplié par 0.8, arrondi à l'inférieur).

② Affiche salePrices en séparant les valeurs par « , ».

③ Crée un tableau qui ne contient que les noms des produits, et affiche-le en séparant les noms par « / ».

④ Affiche le price du premier élément de bakeryItems.

(Si tout s'exécute correctement, une explication apparaîtra.)

Éditeur JavaScript / TypeScript

Exécuter le code pour voir le résultat

Ne garder que les éléments qui correspondent — filter et la taille du résultat

Imagine que tu veux lister uniquement les livres en stock. Comme map ne change jamais le nombre d'éléments, il ne peut pas écarter ceux qui sont en rupture, et tu te retrouves à écrire un if dans un forEach pour appeler push.

filter (une méthode qui renvoie un nouveau tableau contenant uniquement les éléments pour lesquels le callback a renvoyé true) se charge de ce filtrage. La fonction que tu lui passes reçoit un élément et renvoie un booléen qui indique s'il faut le garder.

const books = [
  { title: "SQL en pratique", stock: 0 },
  { title: "Les bases du réseau", stock: 5 },
  { title: "Linux illustré", stock: 2 },
];

// Une fonction qui renvoie un booléen : on garde ou pas
const isInStock = (book) => book.stock > 0;

// Seuls les éléments qui ont renvoyé true entrent dans le nouveau tableau
const available = books.filter(isInStock);
console.log(available.length);      // 2
console.log(available[0].title);    // Les bases du réseau
console.log(books.length);          // 3
Les éléments restants se décalent vers l'avant
books index 0stock 0stock > 0 vautfalsePas ajoutéà availablebooks index 1stock 5stock > 0 vauttrueavailableindex 0books index 2stock 2stock > 0 vauttrueavailableindex 1
Seuls les 2 éléments qui ont renvoyé true entrent dans available. La taille baisse d'autant d'éléments qu'il y en a d'écartés, et ceux qui restent se décalent vers l'avant pour combler les trous.

available ne connaît que les nouvelles positions : l'index que chaque élément avait dans books est perdu. Tu verras à la fin de cet article comment travailler avec la position d'origine. Le tableau ci-dessous résume comment chacune des 3 méthodes utilise la valeur de retour de la fonction que tu lui passes.

MéthodeValeur de retour du callbackTaille du tableau obtenu
mapVa dans le nouveau tableauIdentique à l'original
filterSi true, l'élément est gardéIdentique à l'original ou plus petite
forEachIgnoréeAucun tableau créé (renvoie undefined)

Avec des accolades, il faut un return

Si tu écris le corps entre accolades, comme dans (book) => { book.stock > 0 }, la valeur de retour est undefined tant que tu n'écris pas return. Comme undefined est falsy, aucun élément n'est gardé et tu obtiens un tableau vide. Pour une vérification tenant sur une ligne, omets les accolades.

Sur le site d'une jardinerie, tu répartis les plantes d'intérieur selon la taille du pot. plants est déjà déclaré.

① Crée un tableau largePots qui ne garde que les produits en grand pot, et affiche combien il y en a.

② Affiche les noms des produits restés dans largePots, séparés par « , ».

③ Crée un tableau smallPots qui ne garde que les produits en petit pot, et affiche leurs noms séparés par « , ».

④ Affiche le nombre d'éléments de plants.

Éditeur JavaScript / TypeScript

Exécuter le code pour voir le résultat

Filtrer, puis transformer — chaîner filter et map

Imagine que tu veux transformer en chaînes à afficher uniquement les cours publiés. Tu pourrais ranger le résultat de filter dans une variable, puis appeler .map(...) dessus sur une deuxième ligne, mais tu donnerais alors un nom à un tableau intermédiaire dont tu ne te serviras plus jamais.

filter renvoie lui aussi un tableau : tu peux donc appeler .map(...) directement sur sa valeur de retour. Enchaîner ainsi les appels de méthodes s'appelle le chaînage (appeler la méthode suivante sur la valeur de retour de la précédente).

const courses = [
  { title: "Débuter avec Git", minutes: 45, published: true },
  { title: "Exercices SQL", minutes: 90, published: false },
  { title: "Linux pour débutants", minutes: 60, published: true },
];

// Ne garde que les cours publiés, puis transforme le résultat en chaînes à afficher
// Un . en début de ligne appelle la méthode sur le résultat de la ligne précédente (c'est toujours une seule expression)
const labels = courses
  .filter((course) => course.published)   // published est déjà un booléen, on le renvoie tel quel
  .map((course) => `${course.title} (${course.minutes} min)`);

console.log(labels.join(" / "));
// Débuter avec Git (45 min) / Linux pour débutants (60 min)
console.log(labels.length);    // 2
console.log(courses.length);   // 3
Les 3 tableaux d'une chaîne
courses3 objetsL'originalgarde ses 3.filter(...)2 objetsJamais nommé.map(...)2 chaînesRangédans labels
.map lit le tableau renvoyé par filter sans que ce tableau reçoive jamais de nom. Chaque étape de la chaîne crée un nouveau tableau.

Pour inspecter le résultat intermédiaire, range le résultat de filter dans une variable, affiche-le avec console.log, puis appelle .map(...) sur cette variable. Une fois la vérification terminée, tu peux revenir à la forme chaînée.

Chaque élève reçoit 5 points de bonus pour avoir suivi un cours de rattrapage. Tu compares chaque note ajustée avec la note minimale de 75. records est déjà déclaré.

① Avec un chaînage, crée un tableau passedScores qui ajoute 5 points à la note de chacun, puis ne garde que les notes de 75 ou plus.

② Affiche passedScores en séparant les valeurs par « , ».

③ Affiche le score du deuxième élément de records.

④ Avec un chaînage, récupère le nom de ceux qui atteignent 75 ou plus après le bonus, et affiche-les séparés par « / ».

Éditeur JavaScript / TypeScript

Exécuter le code pour voir le résultat

Récupérer la position — l'index du callback

Imagine que tu veux ajouter un rang, comme « n° 1 » ou « n° 2 », à un classement. Les éléments eux-mêmes ne stockent pas de rang : on est donc tenté de garder un compteur en dehors du callback et de lui ajouter 1 à chaque appel.

map et filter passent l'élément en premier argument du callback, et la position de cet élément (son index, compté à partir de 0) en deuxième. Les arguments sont transmis dans l'ordre, donc même si tu n'as besoin que de l'index, tu dois quand même écrire le premier paramètre. Si tu nommes ce deuxième paramètre index, tu peux t'en servir aussi bien pour afficher un rang que pour vérifier si un élément fait partie des premiers.

const ranking = ["Écouteurs sans fil", "Batterie externe", "Support de téléphone", "Hub USB"];

// Le deuxième argument est la position, comptée à partir de 0
const lines = ranking.map((item, index) => `N° ${index + 1} : ${item}`);
console.log(lines.join("\n"));
// N° 1 : Écouteurs sans fil
// N° 2 : Batterie externe
// N° 3 : Support de téléphone
// N° 4 : Hub USB

// Le callback de filter reçoit les deux mêmes arguments
const topThree = ranking.filter((item, index) => index < 3);
console.log(topThree.length);   // 3
Deux façons d'utiliser le même index
map reçoit(item, index)Les 4 élémentsreçoivent 0 à 3index + 1 entredans la chaîneToujours 4,désormais classésfilter reçoit(item, index)Les mêmes 0 à 3que mapGardési index < 3Plus que 3,valeurs intactes
Les deux callbacks reçoivent la position, comptée à partir de 0, en deuxième argument. map intègre la position dans la nouvelle valeur, filter s'en sert pour décider quoi garder.

L'index passé à filter est la position dans le tableau d'origine. Le fait que les éléments gardés se décalent ensuite vers l'avant n'a aucun effet sur la vérification : index < 3 désigne donc les 3 premiers éléments du tableau d'origine.

Une page thématique affiche des articles dans l'ordre recommandé. articles et viewCounts sont déjà déclarés, et viewCounts contient le nombre de vues de chaque article, dans le même ordre.

① À l'aide de viewCounts, crée un tableau de chaînes de la forme « Titre (N vues) » et affiche-les une par ligne.

② Ne garde que les articles qui ont 5000 vues ou plus, et affiche-les séparés par « , ».

③ Ne garde que les articles classés à un rang pair, comme le 2e et le 4e, et affiche-les séparés par « , ».

Éditeur JavaScript / TypeScript

Exécuter le code pour voir le résultat
QUIZ

Vérification des connaissances

Répondez à chaque question une par une.

Question 1Après avoir exécuté prices.map((price) => price * 2), que devient le tableau prices d'origine ?

Question 2Dans courses.filter(isPublished).map(toLabel), quel tableau map lit-il ?

Question 3Dans le callback de ranking.map((item, index) => ...), qu'est-ce qui est passé à index ?