`Objet` Array
Cet article explique l'objet Array.
J'expliquerai étape par étape les utilisations pratiques des tableaux d'une manière simple à comprendre.
YouTube Video
Objet Array
L'objet Array de JavaScript est l'une des structures les plus importantes qui constitue la base de tous les types de traitement de données. Des opérations de base sur les tableaux aux fonctions d'ordre supérieur utiles pour des transformations de données efficaces, il existe de nombreuses fonctionnalités à connaître.
Bases des tableaux
En JavaScript, les tableaux sont une structure de données fondamentale pour gérer plusieurs valeurs ensemble. Ici, nous présentons comment créer des tableaux et comment lire et écrire leurs éléments à l'aide d'exemples simples.
1// Create arrays in different ways
2const arr1 = [1, 2, 3]; // array literal
3const arr2 = new Array(4, 5, 6); // Array constructor
4const arr3 = Array.of(7, 8, 9); // Array.of
5
6console.log("arr1 created with literal. :", arr1);
7console.log("arr2 created with constructor:", arr2);
8console.log("arr3 created with Array.of. :", arr3);
9// arr1 created with literal. : [ 1, 2, 3 ]
10// arr2 created with constructor: [ 4, 5, 6 ]
11// arr3 created with Array.of. : [ 7, 8, 9 ]
12
13// Access and modify elements
14let first = arr1[0]; // read element
15console.log("First element of arr1:", first);
16// First element of arr1: 1
17
18arr1[1] = 20; // modify element
19console.log("arr1 after modifying index 1:", arr1);
20// arr1 after modifying index 1: [ 1, 20, 3 ]
21
22const len = arr1.length; // get length
23console.log("Length of arr1:", len);
24// Length of arr1: 3
- Ce code montre trois façons de créer des tableaux, comment lire et mettre à jour les éléments à l'aide des indices, et comment obtenir la longueur avec la propriété
length. - Les littéraux de tableau sont les plus courants et les plus lisibles, et sont utilisés le plus fréquemment dans les situations quotidiennes.
Ajout et suppression d'éléments (à la fin ou au début)
Les tableaux vous permettent d'ajouter ou de supprimer facilement des éléments à la fin ou au début. Ces opérations sont également utiles lors de la mise en œuvre de structures comme les piles ou les files d'attente.
1// Push and pop (stack-like)
2const stack = [];
3console.log("Initial stack:", stack);
4
5stack.push(1); // push 1
6console.log("After push(1):", stack);
7
8stack.push(2); // push 2
9console.log("After push(2):", stack);
10
11const last = stack.pop(); // pop -> 2
12console.log("Popped value:", last);
13console.log("Stack after pop():", stack);
14
15// Unshift and shift (queue-like)
16const queue = [];
17console.log("Initial queue:", queue);
18
19queue.push('a'); // add to end
20console.log("After push('a'):", queue);
21
22queue.unshift('start'); // add to front
23console.log("After unshift('start'):", queue);
24
25const firstItem = queue.shift(); // remove from front
26console.log("Shifted value:", firstItem);
27console.log("Queue after shift():", queue);
28
29// Initial stack: []
30// After push(1): [ 1 ]
31// After push(2): [ 1, 2 ]
32// Popped value: 2
33// Stack after pop(): [ 1 ]
34
35// Initial queue: []
36// After push('a'): [ 'a' ]
37// After unshift('start'): [ 'start', 'a' ]
38// Shifted value: start
39// Queue after shift(): [ 'a' ]
pushetpopagissent sur la fin d'un tableau. Ils sont idéaux pour la mise en œuvre des structures de pile.unshiftetshiftagissent sur le début d'un tableau. Cependant, sachez qu'agir sur le début nécessite de décaler tous les indices des éléments en interne, ce qui rend cette opération coûteuse.
Gestion des éléments au milieu (splice et slice)
Pour manipuler les éléments au milieu d'un tableau, choisissez entre splice et slice selon que vous souhaitiez modifier le tableau original ou non. Si vous souhaitez simplement extraire une partie d’un tableau, utilisez slice ; si vous souhaitez modifier le tableau lui-même, par exemple en insérant ou en supprimant des éléments, utilisez splice.
1// slice (non-destructive)
2const nums = [0, 1, 2, 3, 4];
3console.log("Original nums:", nums);
4
5const part = nums.slice(1, 4); // returns [1, 2, 3]
6console.log("Result of nums.slice(1, 4):", part);
7console.log("nums after slice (unchanged):", nums);
8
9// splice (destructive)
10const arr = [10, 20, 30, 40];
11console.log("\nOriginal arr:", arr);
12
13// remove 1 item at index 2, insert 25 and 27
14arr.splice(2, 1, 25, 27);
15console.log("After arr.splice(2, 1, 25, 27):", arr);
16
17// Original nums: [ 0, 1, 2, 3, 4 ]
18// Result of nums.slice(1, 4): [ 1, 2, 3 ]
19// nums after slice (unchanged): [ 0, 1, 2, 3, 4 ]
20
21// Original arr: [ 10, 20, 30, 40 ]
22// After arr.splice(2, 1, 25, 27): [ 10, 20, 25, 27, 40 ]
sliceextrait uniquement des éléments et ne modifie pas le tableau original.spliceajoute ou supprime des éléments et modifie le tableau lui-même, il faut donc être particulièrement attentif à son impact sur le comportement.
Itération (for / for...of / forEach)
Il existe plusieurs manières de traiter des tableaux en séquence, et vous pouvez choisir selon votre objectif et vos préférences de style de codage. Voici trois structures de boucle classiques.
1const items = ['apple', 'banana', 'cherry'];
2console.log("Items:", items);
3
4// classic for
5console.log("\n--- Classic for loop ---");
6for (let i = 0; i < items.length; i++) {
7 console.log(`Index: ${i}, Value: ${items[i]}`);
8}
9
10// for...of (recommended for values)
11console.log("\n--- for...of loop ---");
12for (const item of items) {
13 console.log(`Value: ${item}`);
14}
15
16// forEach (functional style)
17console.log("\n--- forEach loop ---");
18items.forEach((item, index) => {
19 console.log(`Index: ${index}, Value: ${item}`);
20});- La boucle
forest la plus flexible, permettant les opérations sur les indices et un contrôle précis de l'itération à l'aide debreaket d'autres instructions. for...ofpropose une manière concise de manipuler les valeurs des éléments et présente le meilleur équilibre en termes de lisibilité.forEachpermet un code de style fonctionnel et convient bien aux opérations à effets de bord, comme la journalisation ou la mise à jour de données pour chaque élément. Cependant, notez que vous ne pouvez pas utiliserbreakoucontinue, et qu'il n'est pas adapté au traitement asynchrone avecawait.
map / filter / reduce — Fonctions d'ordre supérieur
Les fonctions map, filter et reduce sont des fonctions d'ordre supérieur fréquemment utilisées pour transformer, filtrer ou agréger des tableaux. Comme vous pouvez exprimer clairement les traitements répétitifs, votre code devient simple et facile à comprendre.
1const numbers = [1, 2, 3, 4, 5];
2console.log("Original numbers:", numbers);
3
4// map: transform each item
5const doubled = numbers.map(n => n * 2);
6console.log("\nResult of map (n * 2):", doubled);
7
8// filter: select items
9const evens = numbers.filter(n => n % 2 === 0);
10console.log("Result of filter (even numbers):", evens);
11
12// reduce: accumulate to single value
13const sum = numbers.reduce((acc, n) => acc + n, 0);
14console.log("Result of reduce (sum):", sum);
15
16// Original numbers: [ 1, 2, 3, 4, 5 ]
17// Result of map (n * 2): [ 2, 4, 6, 8, 10 ]
18// Result of filter (even numbers): [ 2, 4 ]
19// Result of reduce (sum): 15
- Ces méthodes permettent de se concentrer sur ce que vous souhaitez faire, de manière déclarative, améliorant la lisibilité et aidant à éviter les effets de bord indésirables.
find / findIndex / some / every
Voici un aperçu des méthodes de recherche et de vérification de condition. Elles sont utiles pour trouver des éléments répondant à certaines conditions ou pour effectuer des vérifications booléennes sur l'ensemble.
1const users = [
2 { id: 1, name: 'Alice' },
3 { id: 2, name: 'Bob' },
4 { id: 3, name: 'Carol' }
5];
6
7console.log("Users:", users);
8
9// Find the first user whose name is 'Bob'
10const bob = users.find(user => user.name === 'Bob');
11console.log("\nResult of find (name === 'Bob'):", bob);
12
13// Find index of the user whose id is 3
14const indexOfId3 = users.findIndex(user => user.id === 3);
15console.log("Result of findIndex (id === 3):", indexOfId3);
16
17// Check if there exists a user with id = 2
18const hasId2 = users.some(user => user.id === 2);
19console.log("Result of some (id === 2):", hasId2);
20
21// Check if all users have a numeric id
22const allHaveNumericId = users.every(user => typeof user.id === 'number');
23console.log("Result of every (id is number):", allHaveNumericId);
24// Result of find (name === 'Bob'): { id: 2, name: 'Bob' }
25// Result of findIndex (id === 3): 2
26// Result of some (id === 2): true
27// Result of every (id is number): true
findrenvoie le premier élément qui satisfait la condition.findIndexrenvoie l'indice de l'élément qui satisfait la condition.somerenvoietrues'il existe au moins un élément qui satisfait la condition.everyrenvoietruesi tous les éléments satisfont la condition.
Toutes ces méthodes sont très utiles pour le traitement des tableaux, donc les utiliser de manière appropriée selon la situation gardera votre code concis et clair.
Fonctions de tri et de comparaison
Les tableaux sont triés avec sort, mais par défaut cette méthode compare les éléments comme des chaînes de caractères, ce qui peut entraîner des résultats inattendus lors du tri de nombres.
1const nums = [10, 2, 33, 4];
2console.log("Original nums:", nums);
3
4// Default sort: compares elements as strings (not suitable for numbers)
5nums.sort();
6console.log("\nAfter default sort (string comparison):", nums);
7
8// Numeric ascending sort using a compare function
9nums.sort((a, b) => a - b);
10console.log("After numeric sort (a - b):", nums);
11
12// Sort objects by a property
13const people = [{ age: 30 }, { age: 20 }, { age: 25 }];
14console.log("\nOriginal people:", people);
15
16people.sort((a, b) => a.age - b.age);
17console.log("After sorting people by age:", people);
18
19// After default sort (string comparison): [ 10, 2, 33, 4 ]
20// After numeric sort (a - b): [ 2, 4, 10, 33 ]
21// Original people: [ { age: 30 }, { age: 20 }, { age: 25 } ]
22// After sorting people by age: [ { age: 20 }, { age: 25 }, { age: 30 } ]
- Lors du tri de nombres ou d'objets, spécifiez toujours une fonction de comparaison pour les trier dans l'ordre désiré.
- Dans une fonction de comparaison, une valeur de retour négative place
aavantb, une valeur positive placebavanta, et 0 conserve leur ordre.
Copie de tableaux et immuabilité
Lors de la copie de tableaux, il est important de comprendre la différence entre une « copie par référence » et une « copie superficielle ». En particulier, sachez que si le tableau contient des objets, une copie superficielle entraînera le partage des objets internes.
Copie par référence
Lorsque vous assignez un tableau à une autre variable, le tableau lui-même n'est pas dupliqué ; c'est la « référence » pointant vers le même tableau qui est copiée.
1const a = [1, 2, 3];
2console.log("Original a:", a);
3
4// Reference copy; modifying b also affects a
5const b = a;
6console.log("\nReference copy b = a:", b);
7// Reference copy b = a: [ 1, 2, 3 ]
8
9b[0] = 100;
10console.log("After modifying b[0] = 100:");
11console.log("a:", a); // a: [ 100, 2, 3 ] (affected)
12console.log("b:", b); // b: [ 100, 2, 3 ]
- Avec une copie par référence, si vous modifiez le contenu du tableau en utilisant la variable copiée, ces changements seront également reflétés dans la variable originale qui fait référence au même tableau.
Copie superficielle
Utiliser slice() ou la syntaxe de l'opérateur de propagation crée une « copie superficielle » car seules les valeurs sont dupliquées ; les tableaux original et copié sont alors traités comme des entités distinctes.
1const a = [1, 2, 3];
2console.log("Original a:", a);
3
4// Shallow copy (new array)
5const b = a.slice();
6console.log("\nShallow copy using slice():", b);
7// Shallow copy using slice(): [ 100, 2, 3 ]
8
9const c = [...a];
10console.log("Shallow copy using spread [...a]:", c);
11// Shallow copy using spread [...a]: [ 100, 2, 3 ]
12
13// Modifying c or d does NOT affect a
14b[1] = 200;
15c[2] = 300;
16console.log("\nAfter modifying b[1] = 200 and c[2] = 300:");
17console.log("a:", a); // [ 100, 2, 3 ]
18console.log("b:", b); // [ 100, 200, 3 ]
19console.log("c:", c); // [ 100, 2, 300 ]
- Ce code montre qu'une copie superficielle d'un tableau, créée avec
slice()ou la syntaxe de propagation, n'affecte pas le tableau original.
Copie superficielle et immuabilité
Même si vous dupliquez un tableau avec une « copie superficielle », un partage involontaire peut se produire si le tableau contient des objets à l'intérieur.
1// Shallow copy doesn't clone inner objects
2const nested = [{ x: 1 }, { x: 2 }];
3console.log("\nOriginal nested:", nested);
4// Original nested: [ { x: 1 }, { x: 2 } ]
5
6const shallow = nested.slice();
7console.log("Shallow copy of nested:", shallow);
8// Shallow copy of nested: [ { x: 1 }, { x: 2 } ]
9
10// Changing inner object affects both arrays
11shallow[0].x = 99;
12console.log("\nAfter shallow[0].x = 99:");
13console.log("nested:", nested);
14console.log("shallow:", shallow);
15// nested: [ { x: 99 }, { x: 2 } ]
16// shallow: [ { x: 99 }, { x: 2 } ]
- Ce code montre qu'avec une copie superficielle, les objets internes sont partagés, donc la modification de ces objets entraîne des changements dans le tableau original ainsi que dans sa copie.
- Si vous avez besoin de données indépendantes, vous devez effectuer une 'copie profonde', par exemple avec
structuredClone()ou via la conversion JSON.
Méthodes utilitaires utiles
Voici des méthodes utilitaires fréquemment utilisées lors de la manipulation de tableaux. En les utilisant à bon escient selon votre objectif, vous pourrez écrire un code court et lisible.
includes
La méthode includes vérifie si une valeur spécifique est présente dans un tableau.
1// includes: check if array contains a value
2const letters = ['a', 'b', 'c'];
3const hasB = letters.includes('b');
4console.log("letters:", letters); // [ 'a', 'b', 'c' ]
5console.log("letters.includes('b'):", hasB); // true
- Dans ce code, la méthode
includesest utilisée pour déterminer simplement si une valeur donnée existe dans le tableau.
concat
La méthode concat renvoie un nouveau tableau qui ajoute à la fin le tableau ou les valeurs spécifiés, tout en conservant le tableau d'origine inchangé.
1// concat: merge arrays without mutation
2const a1 = [1, 2];
3const a2 = [3, 4];
4const combined = a1.concat(a2);
5console.log("a1.concat(a2):", combined); // [ 1, 2, 3, 4 ]
6console.log("a1(unchanged):", a1); // [ 1, 2 ]
- Ce code montre que
concatn'est pas destructif, ce qui vous permet de générer un nouveau tableau tout en préservant l'original.
flat
En utilisant la méthode flat, vous pouvez aplatir des tableaux imbriqués.
1// flat and flatMap: flatten arrays or map + flatten in one step
2const nested = [1, [2, [3]]];
3console.log("nested:", nested); // nested: [ 1, [ 2, [ 3 ] ] ]
4console.log("nested.flat():", nested.flat()); // default depth = 1
5// nested.flat(): [ 1, 2, [ 3 ] ]
- Ce code montre le résultat de l'aplatissement d'un tableau d'un niveau.
- Comme
flatpermet de spécifier la profondeur, vous pouvez gérer la structure imbriquée de façon flexible selon vos besoins.
flatMap
La méthode flatMap applique une transformation à chaque élément, puis aplatit automatiquement les résultats dans un tableau à une dimension.
1// flat and flatMap: flatten arrays or map + flatten in one step
2const words = ['hello world', 'hi'];
3console.log("words:", words); // [ 'hello world', 'hi' ]
4
5const splitWords = words.flatMap(w => w.split(' '));
6console.log("words.flatMap(w => w.split(' ')):", splitWords);
7// words.flatMap(w => w.split(' ')): [ 'hello', 'world', 'hi' ]
- Ce code montre un exemple où chaque chaîne du tableau est divisée par les espaces, puis les résultats sont combinés et aplatis dans un seul tableau.
join
La méthode join crée une chaîne de caractères en concaténant les éléments d'un tableau à l'aide d'un séparateur spécifié.
1// join: combine elements into a string with a separator
2const csv = ['a', 'b', 'c'].join(',');
3console.log("['a', 'b', 'c'].join(','):", csv); // a,b,c
- Dans ce code, la méthode
joinest utilisée pour convertir un tableau en une chaîne séparée par des virgules.
Pièges courants
Les opérations sur les tableaux peuvent sembler simples à première vue, mais plusieurs points peuvent facilement conduire à un comportement inattendu. De nombreux pièges passent inaperçus lors des manipulations de tableaux au quotidien, donc garder en tête les points suivants améliorera considérablement la fiabilité de votre code.
- Le
sort()d'Arraytrie par comparaison de chaînes par défaut. Pour trier correctement les nombres, vous devez toujours fournir une fonction de comparaison. - Copier des tableaux (avec
sliceou la syntaxe de décomposition, etc.) crée une copie superficielle. Si vos tableaux contiennent des objets, soyez prudent car les données originales peuvent être modifiées involontairement. spliceest une méthode destructive qui change directement le tableau, alors quesliceest une méthode non destructive qui ne modifie pas le tableau d'origine. Il est important de les utiliser de manière adaptée à vos besoins.forEachn'est pas adapté aux boucles avec traitement asynchrone utilisantawait. Si vous souhaitez exécuter de manière sûre un traitement asynchrone dans l'ordre, il est recommandé d'utiliserfor...of.
Exemple pratique
Voici un exemple qui combine les méthodes de tableau pour 'obtenir l'âge total à partir de données utilisateur, extraire ceux qui ont 30 ans ou plus, et créer une liste de noms.'.
1const users = [
2 { name: 'Alice', age: 28 },
3 { name: 'Bob', age: 34 },
4 { name: 'Carol', age: 41 },
5 { name: 'Dave', age: 19 }
6];
7
8console.log("Users:", users);
9
10// sum ages
11const totalAge = users.reduce((acc, u) => acc + u.age, 0);
12console.log("\nTotal age of all users:", totalAge);
13// Total age of all users: 122
14
15// filter and map names of users 30 and older
16const namesOver30 = users
17 .filter(u => u.age >= 30)
18 .map(u => u.name);
19
20console.log("Users aged 30 or older (names):", namesOver30);
21// Users aged 30 or older (names): [ 'Bob', 'Carol' ]
- En enchaînant
reduce,filteretmap, vous pouvez facilement écrire l'agrégation, l'extraction de condition et la transformation des données. - Un tel 'pipeline de traitement de données' est très lisible et, en raison de son style à faibles effets de bord, est souvent utilisé dans les applications réelles.
Résumé
Avec les tableaux en JavaScript, même les opérations de base sont largement applicables et, en utilisant des fonctions d'ordre supérieur, votre code devient plus concis et expressif. Il y a beaucoup de choses à comprendre, mais une fois que vous maîtrisez l'utilisation adéquate de chaque méthode, le traitement des données devient bien plus fluide.
Vous pouvez suivre l'article ci-dessus avec Visual Studio Code sur notre chaîne YouTube. Veuillez également consulter la chaîne YouTube.