Travailler avec des tableaux en JavaScript signifie rarement que vous devez encore écrire une boucle for. Le langage vous offre désormais des outils expressifs qui transforment des données imbriquées et confuses en transformations propres et lisibles. Ce guide reprend là où les bases s'arrêtent — en se concentrant sur l'aplatissement, la réduction, l'interrogation et les nouveaux modèles immuables qui laissent vos données d'origine intactes.
Aplatir les structures imbriquées avec flat() et flatMap()
Les données du monde réel adorent l'imbrication. Que vous récupériez des enregistrements via une API, que vous analysiez un CSV ou que vous lisiez un fichier de configuration, vous vous retrouverez souvent avec des tableaux à l'intérieur de tableaux. flat() existe pour réduire ces couches en une seule liste linéaire.
Appelez flat() sur un tableau imbriqué et, par défaut, il aplatit un seul niveau de profondeur :
const nested = [[1, 2], [3, 4]];
const flat = nested.flat();
// [1, 2, 3, 4]
Le tableau nested d'origine reste exactement tel quel. Si votre imbrication est plus profonde, passez un argument de profondeur tel que flat(2) ou même flat(Infinity) si vous ne connaissez pas le nombre de couches existantes. Cela vaut bien mieux que d'écrire soi-même des fonctions d'aide récursives.
Lorsque vous devez transformer des éléments et aplatir le résultat, flatMap() fait les deux en un seul passage. Il applique une fonction de mapping à chaque élément, puis aplatit les tableaux retournés d'un niveau. Faire cela avec des appels séparés .map().flat() fonctionne, mais flatMap() évite la création du tableau intermédiaire, ce qui économise de la mémoire et une seconde itération. Un cas d'utilisation classique consiste à diviser un tableau de phrases en mots individuels :
const lines = ['hello world', 'foo bar'];
const words = lines.flatMap(line => line.split(' '));
// ['hello', 'world', 'foo', 'bar']
Filtrage et mapping : connaissez la différence
filter() crée un nouveau tableau contenant uniquement les éléments qui passent votre test. Le callback doit retourner une valeur truthy pour conserver l'élément, ou une valeur falsy pour le supprimer. Il ne mutera jamais le tableau source.
const users = [
{ name: 'Ajay', active: true },
{ name: 'Bina', active: false }
];
const activeUsers = users.filter(u => u.active);
Il est facile de confondre filter() avec map() car les deux renvoient de nouveaux tableaux et acceptent des callbacks. La différence réside dans le contrat. map() renvoie un nouveau tableau de la même longueur. Transformez chaque élément, effectuez une vérification conditionnelle, et vous verrez effectivement true ou false pour chaque emplacement, car map() ne supprime jamais d'éléments — il ne fait que les modifier. filter(), en revanche, renvoie un tableau potentiellement plus court contenant uniquement les éléments qui passent votre test. Si vous voulez supprimer des données, tournez-vous vers filter(). Si vous voulez transformer chaque donnée tout en préservant le nombre d'éléments, utilisez map().
Réduire les tableaux avec reduce()
Alors que map() et filter() renvoient des tableaux, reduce() réduit une liste entière à une seule valeur. Il parcourt la liste de gauche à droite, en transportant un accumulateur à chaque étape.
const totals = [10, 20, 30];
const sum = totals.reduce((acc, current) => acc + current, 0);
// 60
Le deuxième argument — 0 dans l'exemple ci-dessus — est la valeur initiale de l'accumulateur. Si vous l'omettez, JavaScript utilise le premier élément comme point de départ, en commençant l'itération à l'index 1. Cela piège souvent les développeurs lorsque le tableau est vide, car appeler reduce() sans valeur initiale sur un tableau vide provoque une TypeError.
reduce ne sert pas qu'à l'arithmétique. Vous pouvez l'utiliser pour construire des tables de fréquence, fusionner des objets, trouver des valeurs maximales ou faire passer des données à travers de multiples transformations. Comme il est très flexible, il est préférable de garder le callback court et de nommer vos variables clairement. Un bloc reduce() trop dense devient rapidement plus difficile à lire que la boucle qu'il a remplacée.
Requêtes booléennes : every() et some()
Parfois, vous n'avez pas besoin d'un nouveau tableau ; vous avez besoin d'une réponse par oui ou par non concernant la collection. every() renvoie true uniquement si tous les éléments satisfont votre condition. Si le tout premier élément échoue, il s'arrête immédiatement et renvoie false.
const scores = [85, 92, 78];
const allPassed = scores.every(s => s >= 50);
some() est l'image miroir. Il renvoie true si au moins un élément passe le test. S'il trouve une correspondance, il court-circuite sur le champ.
const hasPerfectScore = scores.some(s => s === 100);
Ces méthodes remplacent les boucles manuelles pour les vérifications de validation. Vous devez confirmer que chaque champ d'un formulaire est rempli ? Utilisez every(). Vous devez savoir si un utilisateur possède des droits d'administrateur ? Utilisez some().
Inspecter et mettre à jour sans mutation
Le JavaScript moderne s'appuie fortement sur l'immuabilité. Ces méthodes vous aident à inspecter ou à mettre à jour des tableaux sans toucher à l'original.
keys() renvoie un objet itérateur contenant les indices du tableau. entries() renvoie un itérateur de paires [index, valeur]. Ils sont très utiles dans les boucles for...of lorsque vous souhaitez une décomposition (destructuring) propre :
const fruits = ['apple', 'banana'];
for (const [index, fruit] of fruits.entries()) {
console.log(index, fruit);
}
with() est un ajout récent au langage qui remplace un élément à un index spécifique et renvoie un tout nouveau tableau. Il répond au même besoin que l'affectation par crochets — arr[index] = value — à la différence qu'il laisse le tableau source intact. Dans des frameworks comme React, où la mutation directe casse la détection de changements, with() est l'option la plus sûre :
const original = [10, 20, 30];
const updated = original.with(1, 99);
// original: [10, 20, 30]
// updated: [10, 99, 30]
Spread et Rest : deux directions pour ...
La syntaxe de décomposition (spread) et le paramètre de reste (rest) partagent les mêmes trois points, mais ils déplacent les données dans des directions opposées.
Utilisez l'opérateur de décomposition (spread) pour décomposer un tableau en éléments individuels. C'est la méthode la plus propre pour fusionner des tableaux ou les cloner :
const front = [1, 2];
const back = [3, 4];
const merged = [...front, ...back];
const copy = [...merged];
Vous pouvez également ajouter facilement des éléments supplémentaires : [0, ...front, 99, ...back].
Le paramètre de reste (rest) regroupe plusieurs éléments dans un seul tableau. À l'intérieur d'une signature de fonction, il collecte tous les arguments restants :
function logFirst(first, ...rest) {
console.log('First:', first);
console.log('Rest:', rest);
}
logFirst('a', 'b', 'c');
// First: a
// Rest: ['b', 'c']
Ensemble, ils permettent de gérer la tâche courante consistant à accepter des entrées variadiques, à les traiter comme un tableau, puis à les redistribuer dans des appels de fonction ou de nouvelles structures de données.
L'essentiel à retenir
L'objectif de ces méthodes n'est pas d'impressionner vos collègues avec des lignes de code ultra-concises. Il s'agit de rendre votre intention explicite. Lorsqu'un autre développeur voit filter(), il sait que vous supprimez des éléments. Lorsqu'il voit reduce(), il sait que vous condensez des données. Lorsqu'il voit with(), il sait que le tableau d'origine est toujours intact quelque part. Maîtriser ces outils signifie écrire du code qui s'explique de lui-même, et qui fait gagner bien plus de temps que n'importe quelle boucle astucieuse.
