Cours JavaScript et TypeScript · Module 2: Fonctions, tableaux et objets

Itération sur les tableaux (forEach, map, filter, reduce)

14 min de lecture
Dans cette leçon9

Dans notre Jurassic Park, nous accumulons une quantité énorme de données sur les dinosaures - leurs espèces, leurs dimensions, leurs habitudes alimentaires, leurs niveaux d'agressivité et bien d'autres choses encore. Dès que nous disposons d'un tableau rempli de telles informations, nous devons presque toujours effectuer une opération sur chacun de ses éléments. JavaScript propose toute une série de méthodes d'itération sur les tableaux qui facilitent énormément ce traitement des données. Chacune a sa spécialité et savoir laquelle choisir fait toute la différence entre un rapport de sécurité limpide et un code que personne ne veut relire.

La méthode forEach

La méthode forEach permet d'exécuter une fonction pour chaque élément du tableau. C'est la manière la plus élémentaire d'itérer : on l'utilise lorsque nous voulons simplement déclencher une action - afficher, enregistrer, prévenir l'équipe - sans avoir besoin de récupérer un nouveau tableau en retour. La fonction que tu lui passes reçoit l'élément courant et, si tu en as besoin, son index et le tableau complet.

1// Tableau contenant les objets qui representent les dinosaures de notre parc
2const dinosaurs = [
3  { name: "Tyrannosaurus", diet: "carnivore", aggressionLevel: 10, dangerous: true },
4  { name: "Triceratops", diet: "herbivore", aggressionLevel: 4, dangerous: false },
5  { name: "Velociraptor", diet: "carnivore", aggressionLevel: 9, dangerous: true },
6  { name: "Stegosaurus", diet: "herbivore", aggressionLevel: 2, dangerous: false },
7  { name: "Brachiosaurus", diet: "herbivore", aggressionLevel: 1, dangerous: false }
8];
9
10// On utilise forEach pour afficher les noms de tous les dinosaures
11console.log("Liste des dinosaures du parc:");
12dinosaurs.forEach(function(dino) {
13  console.log(dino.name);
14});
15
16// On peut aussi utiliser une fonction flechee (arrow function) pour une ecriture plus concise
17console.log("Liste des dinosaures avec leur agression:");
18dinosaurs.forEach(dino => {
19  console.log(`${dino.name} - agression ${dino.aggressionLevel}/10`);
20});

La méthode forEach ne renvoie aucune valeur - plus exactement, forEach() retourne undefined. On s'en sert donc uniquement quand on veut agir sur les éléments, jamais quand on attend un résultat à réutiliser. Remarque au passage le champ dangerous de nos fiches : c'est le drapeau que le protocole du parc lève pour les prédateurs sous surveillance renforcée, et il nous resservira à la fin de la leçon.

La méthode map

La méthode map crée un nouveau tableau contenant les résultats de l'appel de la fonction fournie sur chaque élément du tableau d'origine. Elle est incroyablement utile quand nous voulons transformer des données d'une forme vers une autre : passer d'une liste de fiches complètes à une simple liste de noms, ou à une liste de rapports prêts à être affichés au poste de contrôle.

1// On utilise map pour creer un nouveau tableau contenant uniquement les noms des dinosaures
2const dinoNames = dinosaurs.map(dino => dino.name);
3console.log("Noms des dinosaures:", dinoNames);
4// Resultat: ["Tyrannosaurus", "Triceratops", "Velociraptor", "Stegosaurus", "Brachiosaurus"]
5
6// On peut aussi construire des transformations plus complexes
7const dinoSummaries = dinosaurs.map(dino => {
8  return {
9    name: dino.name,
10    dangerLevel: dino.diet === "carnivore" ? "Eleve" : "Faible",
11    warning: dino.aggressionLevel > 7 ? "Prudence maximale requise!" : "Procedures de securite standard"
12  };
13});
14
15console.log("Bilan de securite des dinosaures:", dinoSummaries);

Contrairement à forEach, la méthode map renvoie toujours un nouveau tableau, de la même longueur que l'original, contenant les éléments transformés. C'est là que se trouve la principale différence entre les deux : map() retourne un nouveau tableau, tandis que forEach() retourne undefined. Ce n'est donc pas une question de vitesse - map() n'est pas plus rapide que forEach(), les deux parcourent exactement le même nombre d'éléments. Ce n'est pas non plus une question d'interruption : ni forEach() ni map() ne peuvent être interrompues, car break est interdit dans une callback et un return ne termine que l'itération en cours. Et non, elles ne fonctionnent pas de manière identique : appeler forEach en espérant récupérer un tableau te laissera avec undefined entre les mains.

La méthode filter

La méthode filter crée un nouveau tableau contenant uniquement les éléments qui satisfont une condition donnée. C'est l'outil idéal lorsque nous devons extraire un sous-ensemble d'éléments d'une collection plus large : les carnivores parmi tous les dinosaures, les enclos à inspecter parmi tous les enclos, les fiches à transmettre au vétérinaire parmi toutes les fiches.

1// On filtre pour obtenir uniquement les dinosaures carnivores
2const carnivores = dinosaurs.filter(dino => dino.diet === "carnivore");
3console.log("Dinosaures carnivores:", carnivores);
4
5// On peut combiner les conditions pour construire des filtres plus complexes
6const dangerousDinos = dinosaurs.filter(dino => {
7  return dino.diet === "carnivore" && dino.aggressionLevel > 7;
8});
9console.log("Dinosaures les plus dangereux:", dangerousDinos);
10
11// On peut aussi combiner filter et map: filtrer en premier, transformer ensuite
12const safetyWarnings = dinosaurs
13  .filter(dino => dino.aggressionLevel > 5)
14  .map(dino => `ALERTE: ${dino.name} a une agression elevee!`);
15
16console.log("Alertes de securite:", safetyWarnings);

La méthode filter renvoie un nouveau tableau, potentiellement plus court que l'original, qui ne contient que les éléments ayant passé le « filtre » - c'est-à-dire ceux pour lesquels la fonction de test a renvoyé true. Le tableau de départ, lui, n'est jamais modifié : dinosaurs contient toujours ses cinq fiches après l'appel. Retiens bien le dernier exemple : le filtrage vient avant la transformation, et nous verrons plus loin pourquoi cet ordre n'est pas négociable.

La méthode reduce

La méthode reduce est la plus puissante des méthodes d'itération, mais aussi la plus complexe. Elle sert à réduire un tableau à une seule valeur - par exemple une somme. Elle exécute une fonction sur chaque élément du tableau en tenant compte du résultat de l'itération précédente : à chaque tour, tu reçois ce que tu as accumulé jusque-là et l'élément courant, et tu renvoies la nouvelle valeur accumulée.

1// On calcule la somme des valeurs aggressionLevel de tous les dinosaures
2const totalAggressionLevel = dinosaurs.reduce((total, dino) => {
3  return total + dino.aggressionLevel;
4}, 0); // 0 est la valeur initiale
5
6console.log("Agression totale dans le parc:", totalAggressionLevel);
7
8// reduce permet aussi de construire des structures plus riches, par exemple un objet qui regroupe les dinosaures par regime
9const dinosByDiet = dinosaurs.reduce((groups, dino) => {
10  // Si le groupe pour ce regime est absent, on le cree
11  if (!groups[dino.diet]) {
12    groups[dino.diet] = [];
13  }
14
15  // On ajoute le dinosaure au bon groupe
16  groups[dino.diet].push(dino.name);
17
18  return groups;
19}, {}); // {} est la valeur initiale (un objet vide)
20
21console.log("Dinosaures regroupes par regime:", dinosByDiet);
22// Resultat: { "carnivore": ["Tyrannosaurus", "Velociraptor"], "herbivore": ["Triceratops", "Stegosaurus", "Brachiosaurus"] }

La méthode reduce prend deux paramètres :

  1. Une fonction de réduction, qui reçoit elle-même au moins deux paramètres :
    • L'accumulateur : la valeur renvoyée par l'itération précédente (ou la valeur initiale)
    • L'élément courant : l'élément du tableau en cours de traitement
  2. Une valeur initiale (optionnelle, mais vivement recommandée)

Attention aux malentendus les plus fréquents sur cette méthode. reduce() ne supprime aucun élément du tableau et ne réduit pas non plus sa taille : après les deux appels ci-dessus, dinosaurs contient toujours ses cinq dinosaures, intacts. Elle ne trie rien non plus - ranger les éléments par ordre décroissant, c'est le travail de sort(), que nous verrons dans un instant. Le mot « réduire » désigne ici une seule chose : réduire tout un tableau à une seule valeur, qu'il s'agisse d'un nombre, d'une chaîne ou même d'un objet de regroupement comme dinosByDiet.

La méthode find

La méthode find renvoie le premier élément du tableau qui satisfait la condition donnée. C'est ce que tu utilises quand tu cherches une fiche précise et non une collection : le gardien qui demande « ouvre-moi le dossier du Stegosaurus » n'attend pas un tableau, il attend un seul dinosaure.

1// On cherche un dinosaure portant un nom precis
2const stegosaurus = dinosaurs.find(dino => dino.name === "Stegosaurus");
3console.log("Informations sur le Stegosaurus:", stegosaurus);
4
5// On cherche le premier carnivore dont aggressionLevel depasse 8
6const dangerousCarnivore = dinosaurs.find(dino => {
7  return dino.diet === "carnivore" && dino.aggressionLevel > 8;
8});
9console.log("Premier carnivore dangereux:", dangerousCarnivore); // Tyrannosaurus
10
11// findIndex fonctionne pareil, mais renvoie la position au lieu de la fiche
12const stegoIndex = dinosaurs.findIndex(dino => dino.name === "Stegosaurus");
13console.log("Position du Stegosaurus:", stegoIndex); // 3

Contrairement à filter, qui renvoie un tableau de tous les éléments correspondants, find ne renvoie que le premier élément trouvé - et s'arrête immédiatement après. Si aucun élément ne correspond, elle renvoie undefined. Sa sœur findIndex suit exactement la même logique mais renvoie l'index de la première correspondance, ou -1 lorsqu'il n'y en a aucune : pratique quand tu dois ensuite modifier la fiche à cette position.

Les méthodes some et every

Les méthodes some et every renvoient une valeur booléenne et servent à vérifier si les éléments d'un tableau satisfont un critère donné. Elles ne te rendent ni les éléments ni un nouveau tableau : elles répondent simplement true ou false, ce qui est exactement ce dont on a besoin pour piloter une alarme ou un contrôle de sécurité.

some

La méthode some vérifie si au moins un élément du tableau satisfait la condition donnée. Dès qu'elle trouve un élément qui correspond, elle arrête son parcours et renvoie true - inutile de continuer, la réponse ne changera plus.

1// On verifie si le parc contient des dinosaures tres agressifs
2const hasHighAggressionDinos = dinosaurs.some(dino => dino.aggressionLevel > 8);
3console.log("Le parc a-t-il des dinosaures tres agressifs?", hasHighAggressionDinos); // true
4
5// On verifie si le parc contient des dinosaures omnivores
6const hasOmnivores = dinosaurs.some(dino => dino.diet === "omnivore");
7console.log("Le parc a-t-il des dinosaures omnivores?", hasOmnivores); // false

Le premier appel renvoie true parce que le Tyrannosaurus dépasse le seuil dès le premier élément examiné. Le second doit parcourir toute la liste avant de conclure : aucune fiche ne porte le régime "omnivore", donc la réponse est false. C'est d'ailleurs la seule famille de méthodes capable de s'arrêter en cours de route - souviens-toi que forEach et map, elles, vont toujours jusqu'au bout.

every

La méthode every vérifie si tous les éléments du tableau satisfont la condition donnée. Symétriquement, elle s'arrête au premier élément qui échoue au test et renvoie aussitôt false.

1// On verifie si tous les dinosaures ont un aggressionLevel renseigne
2const allHaveAggressionLevel = dinosaurs.every(dino => dino.aggressionLevel !== undefined);
3console.log("Tous les dinosaures ont-ils une agression definie?", allHaveAggressionLevel); // true
4
5// On verifie si tous les dinosaures sont herbivores
6const allHerbivores = dinosaurs.every(dino => dino.diet === "herbivore");
7console.log("Tous les dinosaures sont-ils herbivores?", allHerbivores); // false

Le premier contrôle passe : chacune de nos cinq fiches possède bien un aggressionLevel, donc every parcourt tout le tableau et renvoie true. Le second échoue dès la première fiche - le Tyrannosaurus est carnivore - et renvoie false sans même regarder les suivantes. Retiens la paire : some demande « au moins un ? », every demande « tous ? ».

La méthode sort

La méthode sort ordonne les éléments d'un tableau et renvoie le tableau trié. Par défaut, elle compare les éléments comme des chaînes de caractères, ce qui peut donner des résultats surprenants avec des nombres - 10 se retrouve alors avant 2. C'est pourquoi on lui passe presque toujours une fonction de comparaison.

1// On trie les dinosaures par nom (ordre alphabetique)
2const dinosSortedByName = [...dinosaurs].sort((a, b) => {
3  return a.name.localeCompare(b.name);
4});
5console.log("Dinosaures tries par ordre alphabetique:", dinosSortedByName);
6
7// On trie les dinosaures par aggressionLevel (du moins au plus agressif)
8const dinosSortedByAggression = [...dinosaurs].sort((a, b) => {
9  return a.aggressionLevel - b.aggressionLevel;
10});
11console.log("Dinosaures tries par agression (croissant):", dinosSortedByAggression);
12
13// On trie les dinosaures par aggressionLevel (du plus au moins agressif)
14const mostDangerousFirst = [...dinosaurs].sort((a, b) => {
15  return b.aggressionLevel - a.aggressionLevel;
16});
17console.log("Dinosaures tries par agression (decroissant):", mostDangerousFirst);

Remarque que nous utilisons l'opérateur spread (...) pour créer une copie du tableau avant de le trier, car sort modifie le tableau d'origine - et nous, nous voulons conserver l'ordre initial du registre du parc. C'est la grande exception parmi les méthodes vues jusqu'ici : map, filter et reduce laissent la source intacte, sort non. Et c'est bien sort, et elle seule, qui trie par ordre croissant ou décroissant.

Le chaînage des méthodes

L'une des propriétés les plus puissantes de ces méthodes est la possibilité de les enchaîner pour effectuer des opérations complexes sur les données. Le patron le plus courant du parc combine deux étapes : le filtrage, puis la transformation. Chaque appel s'écrit dans l'ordre où le travail doit se faire, en partant toujours du tableau source.

1// La source, puis le filtrage, puis la transformation
2const dangerousNames = dinosaurs
3  .filter(d => d.dangerous)   // 1. on ne garde que les fiches marquees dangerous
4  .map(d => d.name);          // 2. on ne garde que le nom de chaque fiche retenue
5
6console.log("Noms des dinosaures dangereux:", dangerousNames);
7// Resultat: ["Tyrannosaurus", "Velociraptor"]

Lis cette chaîne de gauche à droite : dinosaurs d'abord, car c'est la source des données, puis .filter(d => d.dangerous) pour le filtrage, et enfin .map(d => d.name) pour la transformation. Cet ordre est obligatoire. Si tu inversais les deux derniers appels, map te rendrait un tableau de chaînes de caractères, et filter chercherait ensuite une propriété dangerous sur un texte : elle vaudrait undefined partout, et tu obtiendrais un tableau vide. On filtre tant qu'on a encore les objets complets, on transforme seulement après.

Une fois ce réflexe acquis, tu peux allonger la chaîne autant que nécessaire et y mêler sort ou reduce.

1// Trouvons les noms de tous les dinosaures herbivores, tries par ordre alphabetique
2const sortedHerbivoreNames = dinosaurs
3  .filter(dino => dino.diet === "herbivore")  // En premier: on filtre
4  .map(dino => dino.name)                     // Ensuite on transforme en tableau de noms
5  .sort();                                    // Enfin on trie par ordre alphabetique
6
7console.log("Herbivores (ordre alphabetique):", sortedHerbivoreNames);
8// Resultat: ["Brachiosaurus", "Stegosaurus", "Triceratops"]
9
10// Calculons la moyenne des valeurs aggressionLevel chez les carnivores
11const averageCarnivoreAggression = dinosaurs
12  .filter(dino => dino.diet === "carnivore")   // En premier: on ne garde que les carnivores
13  .reduce((sum, dino, index, array) => {       // Ensuite on reduit a une moyenne
14    sum += dino.aggressionLevel;               // On ajoute la valeur courante a la somme
15
16    // Si le dernier element est atteint, on divise par la taille du tableau
17    if (index === array.length - 1) {
18      return sum / array.length;
19    }
20
21    return sum;                               // On renvoie la somme partielle
22  }, 0);
23
24console.log("Agression moyenne des carnivores:", averageCarnivoreAggression); // 9.5

Le second exemple montre à quel point reduce est souple : la fonction de réduction reçoit aussi l'index et le tableau complet, ce qui nous permet de détecter la dernière itération et de diviser la somme accumulée au bon moment. Deux carnivores, des agressions de 10 et 9, donc une moyenne de 9.5 - une seule valeur obtenue à partir de tout un tableau, exactement ce à quoi sert reduce.

Résumé

Les méthodes d'itération sur les tableaux sont un outil inestimable pour travailler avec des collections de données en JavaScript. Chacune a sa spécialité :

  • forEach : quand nous voulons effectuer une opération sur chaque élément, sans créer de nouveau tableau (elle retourne undefined)
  • map : quand nous voulons transformer chaque élément du tableau en autre chose (elle retourne un nouveau tableau)
  • filter : quand nous voulons sélectionner un sous-ensemble d'éléments satisfaisant un critère précis
  • reduce : quand nous voulons réduire un tableau à une seule valeur, comme une somme, ou à une nouvelle structure
  • find : quand nous cherchons le premier élément satisfaisant un critère donné
  • some/every : quand nous voulons vérifier si certains ou tous les éléments satisfont un critère
  • sort : quand nous voulons ordonner les éléments du tableau

« Les méthodes d'itération, c'est le briefing du matin au parc », dit le Dr Rex. « forEach pour parcourir la checklist quotidienne, filter pour retenir ceux qui demandent une attention particulière, map pour rédiger les rapports, reduce pour calculer les totaux. Chaque outil a son usage ! »

Dans notre Jurassic Park, ces méthodes sont indispensables pour gérer efficacement les données des dinosaures, surveiller la sécurité et prendre des décisions fondées sur les faits. Retiens surtout ceci : choisis la méthode d'après ce que tu veux obtenir - un effet de bord, un nouveau tableau, un sous-ensemble ou une valeur unique - et le reste du code s'écrira presque tout seul.

Code de cette leçon : index.js
1// Itérations sur les tableaux - Parc Jurassique
2console.log("Itérations Avancées");
3console.log("forEach, map, filter, reduce\n");
4
5// ===========================================
6// DONNÉES DE TEST
7// ===========================================
8const dinosaurs = [
9  { id: 1, name: "Rexy", species: "T-Rex", age: 8, weight: 8000, sector: "A", health: 95, carnivore: true, fed: true },
10  { id: 2, name: "Blue", species: "Velociraptor", age: 5, weight: 80, sector: "B", health: 100, carnivore: true, fed: false },
11  { id: 3, name: "Charlie", species: "Velociraptor", age: 5, weight: 80, sector: "B", health: 85, carnivore: true, fed: false },
12  { id: 4, name: "Delta", species: "Velociraptor", age: 5, weight: 80, sector: "B", health: 45, carnivore: true, fed: true },
13  { id: 5, name: "Trike", species: "Triceratops", age: 7, weight: 9000, sector: "C", health: 70, carnivore: false, fed: true },
14  { id: 6, name: "Stego", species: "Stegosaurus", age: 6, weight: 5000, sector: "C", health: 92, carnivore: false, fed: false },
15  { id: 7, name: "Brachio", species: "Brachiosaurus", age: 10, weight: 50000, sector: "D", health: 88, carnivore: false, fed: true }
16];
17
18// ===========================================
19// 1. forEach() - ITÉRATION CLASSIQUE
20// ===========================================
21console.log("=== 1. forEach() - Itération sans valeur de retour ===");
22
23console.log("\nListe de tous les dinosaures:");
24dinosaurs.forEach((dino, index) => {
25  const icon = dino.carnivore ? "carnivore" : "herbivore";
26  console.log(`  ${index + 1}. ${icon} ${dino.name} (${dino.species})`);
27});
28
29// forEach avec effets de bord
30let totalWeight = 0;
31dinosaurs.forEach(dino => {
32  totalWeight += dino.weight;
33});
34console.log(`\nPoids total: ${totalWeight}kg`);
35
36// ===========================================
37// 2. map() - TRANSFORMATION DE DONNÉES
38// ===========================================
39console.log("\n=== 2. map() - Transformation de chaque élément ===");
40
41// Exemple 1: Extraction simple de valeurs
42const names = dinosaurs.map(dino => dino.name);
43console.log("\nNoms seulement:", names.join(", "));
44
45// Exemple 2: Création de nouveaux objets
46const summaries = dinosaurs.map(dino => ({
47  name: dino.name,
48  type: dino.carnivore ? "Carnivore" : "Herbivore",
49  status: dino.health >= 80 ? "En bonne santé" : "Nécessite de l'attention"
50}));
51
52console.log("\nRésumés:");
53summaries.forEach(s => {
54  console.log(`  ${s.name}: ${s.type}, Statut: ${s.status}`);
55});
56
57// Exemple 3: Calculs
58const healthPercentages = dinosaurs.map(dino => {
59  const healthStatus = dino.health >= 90 ? "*" : dino.health >= 70 ? "OK" : "!";
60  return `${healthStatus} ${dino.name}: ${dino.health}%`;
61});
62
63console.log("\nNiveaux de santé:");
64healthPercentages.forEach(h => console.log(`  ${h}`));
65
66// ===========================================
67// 3. filter() - FILTRAGE
68// ===========================================
69console.log("\n=== 3. filter() - Sélection d'éléments ===");
70
71// Exemple 1: Une seule condition
72const carnivores = dinosaurs.filter(dino => dino.carnivore);
73console.log(`\nCarnivores (${carnivores.length}):`, carnivores.map(d => d.name).join(", "));
74
75// Exemple 2: Plusieurs conditions
76const healthyCarnivores = dinosaurs.filter(dino =>
77  dino.carnivore && dino.health >= 80
78);
79console.log(`Carnivores en bonne santé (${healthyCarnivores.length}):`, healthyCarnivores.map(d => d.name).join(", "));
80
81// Exemple 3: Conditions complexes
82const needsAttention = dinosaurs.filter(dino =>
83  dino.health < 80 || !dino.fed
84);
85console.log(`\nNécessitent de l'attention (${needsAttention.length}):`);
86needsAttention.forEach(dino => {
87  const reasons = [];
88  if (dino.health < 80) reasons.push(`santé: ${dino.health}%`);
89  if (!dino.fed) reasons.push("affamé");
90  console.log(`  ${dino.name}: ${reasons.join(", ")}`);
91});
92
93// Exemple 4: Filtrage par secteur
94const sectorB = dinosaurs.filter(dino => dino.sector === "B");
95console.log(`\nSecteur B (${sectorB.length}):`, sectorB.map(d => d.name).join(", "));
96
97// ===========================================
98// 4. reduce() - AGRÉGATION
99// ===========================================
100console.log("\n=== 4. reduce() - Agrégation de données ===");
101
102// Exemple 1: Somme
103const totalHealthPoints = dinosaurs.reduce((sum, dino) => sum + dino.health, 0);
104console.log(`\nSomme des points de santé: ${totalHealthPoints}`);
105
106// Exemple 2: Moyenne
107const avgHealth = totalHealthPoints / dinosaurs.length;
108console.log(`Santé moyenne: ${avgHealth.toFixed(1)}%`);
109
110// Exemple 3: Recherche du max/min
111const oldest = dinosaurs.reduce((max, dino) =>
112  dino.age > max.age ? dino : max
113);
114console.log(`\nLe plus âgé: ${oldest.name} (${oldest.age} ans)`);
115
116const youngest = dinosaurs.reduce((min, dino) =>
117  dino.age < min.age ? dino : min
118);
119console.log(`Le plus jeune: ${youngest.name} (${youngest.age} ans)`);
120
121// Exemple 4: Regroupement
122const bySector = dinosaurs.reduce((acc, dino) => {
123  if (!acc[dino.sector]) acc[dino.sector] = [];
124  acc[dino.sector].push(dino.name);
125  return acc;
126}, {});
127
128console.log("\nRegroupement par secteur:");
129Object.entries(bySector).sort().forEach(([sector, dinos]) => {
130  console.log(`  Secteur ${sector}: ${dinos.join(", ")}`);
131});
132
133// Exemple 5: Comptage des occurrences
134const speciesCount = dinosaurs.reduce((acc, dino) => {
135  acc[dino.species] = (acc[dino.species] || 0) + 1;
136  return acc;
137}, {});
138
139console.log("\nNombre par espèce:");
140Object.entries(speciesCount).forEach(([species, count]) => {
141  console.log(`  ${species}: ${count}`);
142});
143
144// Exemple 6: Construction d'un objet de statistiques
145const stats = dinosaurs.reduce((acc, dino) => {
146  acc.totalDinos++;
147  acc.totalWeight += dino.weight;
148  acc.totalAge += dino.age;
149  if (dino.carnivore) acc.carnivores++;
150  if (dino.health >= 90) acc.veryHealthy++;
151  return acc;
152}, { totalDinos: 0, totalWeight: 0, totalAge: 0, carnivores: 0, veryHealthy: 0 });
153
154console.log("\nStatistiques globales:");
155console.log(`  Total des dinosaures: ${stats.totalDinos}`);
156console.log(`  Poids total: ${stats.totalWeight}kg`);
157console.log(`  Âge moyen: ${(stats.totalAge / stats.totalDinos).toFixed(1)} ans`);
158console.log(`  Carnivores: ${stats.carnivores}`);
159console.log(`  Très en bonne santé (≥90%): ${stats.veryHealthy}`);
160
161// ===========================================
162// 5. CHAÎNAGE DE MÉTHODES
163// ===========================================
164console.log("\n=== 5. Chaînage de méthodes ===");
165
166// Exemple 1: filter + map + sort
167const topHealthyCarnivores = dinosaurs
168  .filter(d => d.carnivore)
169  .filter(d => d.health >= 80)
170  .map(d => ({ name: d.name, health: d.health }))
171  .sort((a, b) => b.health - a.health)
172  .slice(0, 3);
173
174console.log("\nTOP 3 carnivores en bonne santé:");
175topHealthyCarnivores.forEach((d, i) => {
176  console.log(`  ${i + 1}. ${d.name} - ${d.health}%`);
177});
178
179// Exemple 2: filter + map + reduce
180const heavyHerbivoreWeight = dinosaurs
181  .filter(d => !d.carnivore)
182  .filter(d => d.weight > 1000)
183  .map(d => d.weight)
184  .reduce((sum, weight) => sum + weight, 0);
185
186console.log(`\nPoids total des herbivores lourds: ${heavyHerbivoreWeight}kg`);
187
188// Exemple 3: Traitement complexe
189const sectorReport = Object.entries(
190  dinosaurs
191    .filter(d => d.health >= 70)
192    .reduce((acc, d) => {
193      if (!acc[d.sector]) {
194        acc[d.sector] = { count: 0, avgHealth: 0, totalHealth: 0 };
195      }
196      acc[d.sector].count++;
197      acc[d.sector].totalHealth += d.health;
198      return acc;
199    }, {})
200)
201.map(([sector, data]) => ({
202  sector,
203  count: data.count,
204  avgHealth: (data.totalHealth / data.count).toFixed(1)
205}))
206.sort((a, b) => b.avgHealth - a.avgHealth);
207
208console.log("\nRapport des secteurs (santé ≥70%):");
209sectorReport.forEach(s => {
210  console.log(`  Secteur ${s.sector}: ${s.count} dinosaures, santé moyenne: ${s.avgHealth}%`);
211});
212
213// ===========================================
214// 6. COMPARAISON DES APPROCHES
215// ===========================================
216console.log("\n=== 6. Comparaison des approches ===");
217
218console.log("\nTâche: Trouver les noms des carnivores du secteur B");
219
220// Méthode 1: Boucle for (style impératif)
221const result1 = [];
222for (let i = 0; i < dinosaurs.length; i++) {
223  if (dinosaurs[i].carnivore && dinosaurs[i].sector === "B") {
224    result1.push(dinosaurs[i].name);
225  }
226}
227console.log("Boucle for:", result1);
228
229// Méthode 2: forEach (style en partie fonctionnel)
230const result2 = [];
231dinosaurs.forEach(dino => {
232  if (dino.carnivore && dino.sector === "B") {
233    result2.push(dino.name);
234  }
235});
236console.log("forEach:", result2);
237
238// Méthode 3: filter + map (style fonctionnel)
239const result3 = dinosaurs
240  .filter(d => d.carnivore && d.sector === "B")
241  .map(d => d.name);
242console.log("filter + map:", result3);
243
244// ===========================================
245// 7. EXEMPLE COMPLET
246// ===========================================
247console.log("\n=== 7. Système de rapports - exemple complet ===");
248
249class ParkReportingSystem {
250  constructor(dinosaurs) {
251    this.dinosaurs = dinosaurs;
252  }
253
254  // Tableau de bord principal
255  getDashboard() {
256    const total = this.dinosaurs.length;
257
258    const healthStats = this.dinosaurs.reduce((acc, d) => {
259      if (d.health >= 90) acc.excellent++;
260      else if (d.health >= 70) acc.good++;
261      else acc.poor++;
262      acc.total += d.health;
263      return acc;
264    }, { excellent: 0, good: 0, poor: 0, total: 0 });
265
266    const feedingStats = this.dinosaurs.reduce((acc, d) => {
267      if (d.fed) acc.fed++;
268      else acc.hungry++;
269      return acc;
270    }, { fed: 0, hungry: 0 });
271
272    return {
273      totalDinosaurs: total,
274      avgHealth: (healthStats.total / total).toFixed(1),
275      healthBreakdown: {
276        excellent: healthStats.excellent,
277        good: healthStats.good,
278        poor: healthStats.poor
279      },
280      feeding: feedingStats
281    };
282  }
283
284  // Alerte critique
285  getCriticalAlerts() {
286    return this.dinosaurs
287      .filter(d => d.health < 50 || (d.carnivore && !d.fed))
288      .map(d => ({
289        name: d.name,
290        sector: d.sector,
291        issue: d.health < 50 ? `Santé critique: ${d.health}%` : "Carnivore affamé !",
292        priority: d.health < 50 && d.carnivore && !d.fed ? "EXTRÊME" : "ÉLEVÉE"
293      }))
294      .sort((a, b) => {
295        const priorityOrder = { "EXTRÊME": 0, "ÉLEVÉE": 1 };
296        return priorityOrder[a.priority] - priorityOrder[b.priority];
297      });
298  }
299
300  // Classement de santé
301  getHealthRanking(limit = 5) {
302    return this.dinosaurs
303      .map(d => ({
304        name: d.name,
305        species: d.species,
306        health: d.health,
307        sector: d.sector
308      }))
309      .sort((a, b) => b.health - a.health)
310      .slice(0, limit);
311  }
312
313  // Analyse des secteurs
314  getSectorAnalysis() {
315    const sectorData = this.dinosaurs.reduce((acc, d) => {
316      if (!acc[d.sector]) {
317        acc[d.sector] = {
318          dinosaurs: [],
319          totalHealth: 0,
320          carnivores: 0,
321          unfed: 0
322        };
323      }
324      acc[d.sector].dinosaurs.push(d.name);
325      acc[d.sector].totalHealth += d.health;
326      if (d.carnivore) acc[d.sector].carnivores++;
327      if (!d.fed) acc[d.sector].unfed++;
328      return acc;
329    }, {});
330
331    return Object.entries(sectorData)
332      .map(([sector, data]) => ({
333        sector,
334        count: data.dinosaurs.length,
335        avgHealth: (data.totalHealth / data.dinosaurs.length).toFixed(1),
336        carnivores: data.carnivores,
337        unfed: data.unfed,
338        dinosaurs: data.dinosaurs
339      }))
340      .sort((a, b) => a.sector.localeCompare(b.sector));
341  }
342
343  // Afficher le rapport complet
344  displayFullReport() {
345    console.log("\n" + "=".repeat(70));
346    console.log("PARC JURASSIQUE - RAPPORT GLOBAL");
347    console.log("=".repeat(70));
348
349    // Tableau de bord
350    const dashboard = this.getDashboard();
351    console.log("\nTABLEAU DE BORD");
352    console.log(`   Total des dinosaures: ${dashboard.totalDinosaurs}`);
353    console.log(`   Santé moyenne: ${dashboard.avgHealth}%`);
354    console.log(`   Santé - Excellent: ${dashboard.healthBreakdown.excellent}, Bon: ${dashboard.healthBreakdown.good}, Faible: ${dashboard.healthBreakdown.poor}`);
355    console.log(`   Alimentation - Nourris: ${dashboard.feeding.fed}, Affamés: ${dashboard.feeding.hungry}`);
356
357    // Alertes
358    const alerts = this.getCriticalAlerts();
359    if (alerts.length > 0) {
360      console.log("\nALERTES CRITIQUES");
361      alerts.forEach((alert, i) => {
362        console.log(`   ${i + 1}. [${alert.priority}] ${alert.name} (Secteur ${alert.sector}): ${alert.issue}`);
363      });
364    } else {
365      console.log("\nAucune alerte critique");
366    }
367
368    // Classement
369    const ranking = this.getHealthRanking(5);
370    console.log("\nTOP 5 - CLASSEMENT DE SANTÉ");
371    ranking.forEach((d, i) => {
372      console.log(`   ${i + 1}. ${d.name} (${d.species}) - ${d.health}% - Secteur ${d.sector}`);
373    });
374
375    // Analyse des secteurs
376    const sectorAnalysis = this.getSectorAnalysis();
377    console.log("\nANALYSE DES SECTEURS");
378    sectorAnalysis.forEach(sector => {
379      console.log(`   Secteur ${sector.sector}:`);
380      console.log(`     Dinosaures: ${sector.count} - Santé moyenne: ${sector.avgHealth}%`);
381      console.log(`     Carnivores: ${sector.carnivores}, Affamés: ${sector.unfed}`);
382      console.log(`     Liste: ${sector.dinosaurs.join(", ")}`);
383    });
384  }
385}
386
387// Exécuter le système de rapports
388const reportSystem = new ParkReportingSystem(dinosaurs);
389reportSystem.displayFullReport();
390
391console.log("\nFélicitations ! Tu maîtrises maintenant les itérations avancées !");

Tu as repéré une erreur dans cette leçon ?

Teste tes connaissances

Réponds aux questions de cette leçon. Choisis une réponse pour voir tout de suite si elle est correcte.

  1. 1. Quelle est la principale différence entre map() et forEach() ?

  2. 2. À quoi sert la méthode reduce() sur un tableau ?

Tâches pratiques dans le jeu

  • Tri horizontal

    Range les éléments du filtrage et de la transformation d'un tableau :

Articles utiles