Map, Filter, Reduce en JavaScript : Maîtriser les Tableaux

11 min de lecture JavaScript

Si tu connais les bases des tableaux JavaScript, map(), filter() et reduce() sont les trois méthodes qui vont transformer ta façon d'écrire du code. Fini les boucles for qui accumulent du code, fini les variables temporaires partout — ces trois méthodes permettent de transformer, filtrer et agréger des données en une ligne, de façon lisible et sans effets de bord. Ce guide t'explique chacune en profondeur, avec des exemples concrets tirés du vrai développement front-end.

Avant de commencer : les callbacks

Les trois méthodes fonctionnent sur le même principe : elles prennent une fonction callback en argument, qu'elles appellent pour chaque élément du tableau. Si tu n'es pas à l'aise avec les fonctions en JavaScript, relis d'abord notre article sur les closures et la portée des variables — ça posera les bases nécessaires.

const nombres = [1, 2, 3, 4, 5];

// Callback classique
nombres.forEach(function(n) {
  console.log(n);
});

// Avec une arrow function (syntaxe moderne — préférable)
nombres.forEach(n => console.log(n));

Les trois méthodes qui suivent utilisent toutes la même syntaxe avec arrow function. Une fois que tu comprends l'une, les deux autres sont immédiates.

map() — Transformer chaque élément

map() crée un nouveau tableau en appliquant une fonction à chaque élément. Le tableau original reste intact. C'est la méthode de transformation par excellence.

x2 1 2 2 4 3 6
map() transforme chaque élément — le tableau résultant a toujours le même nombre d'éléments
const nombres = [1, 2, 3, 4, 5];

// ❌ Façon boucle for
const doubles = [];
for (let i = 0; i < nombres.length; i++) {
  doubles.push(nombres[i] * 2);
}

// ✅ Avec map() — une ligne
const doubles = nombres.map(n => n * 2);
// [2, 4, 6, 8, 10]

// Le tableau original est intact
console.log(nombres); // [1, 2, 3, 4, 5]

Le callback de map() reçoit jusqu'à 3 arguments : l'élément, l'index, et le tableau complet.

// Exemples concrets
const prenoms = ['alice', 'bob', 'charlie'];

// Mettre en majuscules
const majuscules = prenoms.map(p => p.toUpperCase());
// ['ALICE', 'BOB', 'CHARLIE']

// Transformer des objets
const utilisateurs = [
  { nom: 'Alice', age: 25 },
  { nom: 'Bob', age: 30 },
];

const noms = utilisateurs.map(u => u.nom);
// ['Alice', 'Bob']

const profilsResume = utilisateurs.map(u => ({
  nom: u.nom,
  majeur: u.age >= 18
}));
// [{ nom: 'Alice', majeur: true }, { nom: 'Bob', majeur: true }]

// Utiliser l'index
const numerotes = prenoms.map((p, i) => `${i + 1}. ${p}`);
// ['1. alice', '2. bob', '3. charlie']

filter() — Garder seulement ce qui correspond

filter() crée un nouveau tableau contenant uniquement les éléments pour lesquels le callback retourne true. C'est la méthode de sélection.

filter(x => x > 2) 1 2 3 4
filter() garde seulement les éléments qui passent le test — vert = passe, rouge = éliminé
const nombres = [1, 2, 3, 4, 5, 6, 7, 8];

// Garder les pairs
const pairs = nombres.filter(n => n % 2 === 0);
// [2, 4, 6, 8]

// Garder les supérieurs à 4
const grands = nombres.filter(n => n > 4);
// [5, 6, 7, 8]

// Filtrer des objets
const produits = [
  { nom: 'Laptop', prix: 1200, stock: 0 },
  { nom: 'Souris', prix: 35, stock: 50 },
  { nom: 'Clavier', prix: 89, stock: 12 },
  { nom: 'Écran', prix: 450, stock: 0 },
];

const disponibles = produits.filter(p => p.stock > 0);
// [{ nom: 'Souris', ... }, { nom: 'Clavier', ... }]

const prixAbordable = produits.filter(p => p.prix < 100 && p.stock > 0);
// [{ nom: 'Souris', ... }, { nom: 'Clavier', ... }]

// Supprimer les doublons avec filter (astuce)
const avecDoublons = [1, 2, 2, 3, 3, 3, 4];
const sansDoublons = avecDoublons.filter((val, i, arr) => arr.indexOf(val) === i);
// [1, 2, 3, 4]

reduce() — Accumuler vers un résultat unique

reduce() est la plus puissante des trois — et la plus déroutante au début. Elle réduit un tableau à une seule valeur en accumulant les éléments un par un. Cette valeur peut être un nombre, une chaîne, un objet, ou même un nouveau tableau.

1 2 3 1 3 6 6
reduce() accumule les éléments un par un jusqu'à un résultat final unique
// Syntaxe
array.reduce((accumulateur, elementCourant) => {
  return nouvelAccumulateur;
}, valeurInitiale);

// Somme d'un tableau
const nombres = [1, 2, 3, 4, 5];
const somme = nombres.reduce((acc, n) => acc + n, 0);
// 0 + 1 = 1, 1 + 2 = 3, 3 + 3 = 6, 6 + 4 = 10, 10 + 5 = 15
console.log(somme); // 15

// Maximum d'un tableau
const max = nombres.reduce((acc, n) => n > acc ? n : acc, -Infinity);
console.log(max); // 5

// Compter les occurrences
const fruits = ['pomme', 'banane', 'pomme', 'cerise', 'banane', 'pomme'];
const comptage = fruits.reduce((acc, fruit) => {
  acc[fruit] = (acc[fruit] || 0) + 1;
  return acc;
}, {});
// { pomme: 3, banane: 2, cerise: 1 }

// Aplatir un tableau de tableaux
const imbriques = [[1, 2], [3, 4], [5, 6]];
const plat = imbriques.reduce((acc, arr) => acc.concat(arr), []);
// [1, 2, 3, 4, 5, 6]

La valeur initiale (deuxième argument) est cruciale. Sans elle, reduce() prend le premier élément comme accumulateur de départ — ce qui peut provoquer des bugs subtils sur des tableaux vides.

Chaîner les trois méthodes

La vraie puissance apparaît quand tu chaînes map(), filter() et reduce() ensemble. Comme chaque méthode retourne un tableau (sauf reduce() qui retourne la valeur accumulée), tu peux les appeler en séquence.

const commandes = [
  { produit: 'Laptop', prix: 1200, quantite: 1, categorie: 'électronique' },
  { produit: 'Souris', prix: 35, quantite: 3, categorie: 'électronique' },
  { produit: 'Bureau', prix: 450, quantite: 1, categorie: 'mobilier' },
  { produit: 'Clavier', prix: 89, quantite: 2, categorie: 'électronique' },
  { produit: 'Chaise', prix: 320, quantite: 1, categorie: 'mobilier' },
];

// Total des commandes électronique seulement
const totalElectronique = commandes
  .filter(c => c.categorie === 'électronique')    // Garder l'électronique
  .map(c => c.prix * c.quantite)                  // Calculer le montant
  .reduce((acc, montant) => acc + montant, 0);    // Sommer

console.log(totalElectronique); // 1200 + 105 + 178 = 1483

En 3 lignes, tu as filtré, transformé et agrégé — sans variable temporaire, sans mutation. Compare avec l'équivalent en boucles :

// ❌ Version boucles for — 8 lignes, une variable muable
let totalElectronique = 0;
for (let i = 0; i < commandes.length; i++) {
  if (commandes[i].categorie === 'électronique') {
    totalElectronique += commandes[i].prix * commandes[i].quantite;
  }
}

// ✅ Version chaînée — 5 lignes, 0 mutation
const totalElectronique = commandes
  .filter(c => c.categorie === 'électronique')
  .map(c => c.prix * c.quantite)
  .reduce((acc, montant) => acc + montant, 0);

Cas d'usage réels : dans une app React

Ces méthodes sont au cœur de tout développement React moderne. Que tu fasses de la manipulation du DOM ou du rendu de composants, tu les utiliseras constamment.

// Composant liste filtrée et transformée
function ListeProduits({ produits, categorieActive, recherche }) {
  const produitsFiltres = produits
    .filter(p => categorieActive === 'tous' || p.categorie === categorieActive)
    .filter(p => p.nom.toLowerCase().includes(recherche.toLowerCase()))
    .map(p => ({
      ...p,
      prixFormate: new Intl.NumberFormat('fr-FR', {
        style: 'currency', currency: 'EUR'
      }).format(p.prix)
    }));

  return (
    <ul>
      {produitsFiltres.map(p => (
        <li key={p.id}>
          {p.nom} — {p.prixFormate}
        </li>
      ))}
    </ul>
  );
}

Les erreurs classiques

1. Oublier le return dans map()

// ❌ Sans return avec des accolades — retourne undefined pour chaque élément
const resultat = [1, 2, 3].map(n => {
  n * 2; // Pas de return !
});
console.log(resultat); // [undefined, undefined, undefined]

// ✅ Arrow function implicite (sans accolades)
const resultat = [1, 2, 3].map(n => n * 2);

// ✅ Avec accolades — return explicite obligatoire
const resultat = [1, 2, 3].map(n => {
  return n * 2;
});

2. Muter le tableau dans map()

const objets = [{ val: 1 }, { val: 2 }, { val: 3 }];

// ❌ Mutation de l'objet original
const modifies = objets.map(o => {
  o.val = o.val * 2; // Mute l'objet original !
  return o;
});
console.log(objets[0].val); // 2 — l'original est modifié

// ✅ Créer un nouvel objet
const modifies = objets.map(o => ({ ...o, val: o.val * 2 }));

3. Oublier la valeur initiale dans reduce()

// ❌ Pas de valeur initiale — crash sur tableau vide
const vide = [];
vide.reduce((acc, n) => acc + n); // TypeError: Reduce of empty array

// ✅ Toujours fournir une valeur initiale
vide.reduce((acc, n) => acc + n, 0); // 0

4. Utiliser forEach quand on veut un résultat

// ❌ forEach ne retourne rien
const doubles = [1, 2, 3].forEach(n => n * 2); // undefined

// ✅ map() retourne un nouveau tableau
const doubles = [1, 2, 3].map(n => n * 2); // [2, 4, 6]

find() et findIndex() — bonus indispensables

Deux méthodes dans la même famille qui complètent parfaitement le trio :

const utilisateurs = [
  { id: 1, nom: 'Alice' },
  { id: 2, nom: 'Bob' },
  { id: 3, nom: 'Charlie' },
];

// find() — retourne le premier élément qui correspond
const bob = utilisateurs.find(u => u.nom === 'Bob');
// { id: 2, nom: 'Bob' }

// findIndex() — retourne l'index du premier élément qui correspond
const indexBob = utilisateurs.findIndex(u => u.nom === 'Bob');
// 1

// some() — au moins un élément correspond ?
const aUnAdmin = utilisateurs.some(u => u.role === 'admin'); // false

// every() — tous les éléments correspondent ?
const tousMajeurs = utilisateurs.every(u => u.age >= 18);

Quand utiliser quoi ?

MéthodeQuand l'utiliserRetourne
map()Transformer chaque élémentNouveau tableau (même longueur)
filter()Sélectionner des éléments selon un critèreNouveau tableau (longueur ≤)
reduce()Agréger en une valeur (somme, objet, etc.)N'importe quel type
find()Trouver un élément spécifiqueL'élément ou undefined
forEach()Effets de bord (log, mutation externe)undefined

Conclusion

map(), filter() et reduce() sont les outils qui font la différence entre du JavaScript débutant et du JavaScript professionnel. Ces méthodes permettent d'écrire du code déclaratif — tu décris ce que tu veux plutôt que comment le faire. Le résultat : moins de bugs, plus de lisibilité, et un code qui se comprend en un coup d'œil.

Les points clés :

Pour aller plus loin sur les subtilités du langage, notre guide sur les closures JavaScript est la prochaine étape naturelle — comprendre la portée lexicale t'aidera à maîtriser les callbacks avancés. Et pour la gestion des données asynchrones dans les tableaux, consulte notre guide sur async/await et les Promises.

La documentation de référence pour chaque méthode : MDN Array.map(), MDN Array.filter(), MDN Array.reduce().

Pour pratiquer directement dans le navigateur, retrouve nos ateliers JavaScript interactifs GoGoKodo — map, filter et reduce sont au programme des exercices sur les tableaux.