Skip to content

4. Les tableaux

Les scripts de ce chapitre se trouvent dans le dossier [tableaux] du projet.

4.1. script [tab-01]

Le script suivant illustre une caractéristique essentielle des tableaux TypeScript/JavaScript : ce sont des objets manipulés par référence (par pointeur), et non des données recopiées à chaque affectation.

'use strict';

// un tableau est un objet manipulé via son adresse
const tab1: number[] = [1, 2, 3];
// copie d'adresses
const tab2: number[] = tab1;
// tab1 et tab2 pointent sur le même tableau
console.log("tab1===tab2 :", tab1 === tab2);
// on peut modifier le tableau en passant indifféremment par tab1 ou tab2
tab2[1] = 10;
console.log("tab1=", tab1);
console.log("tab2=", tab2);
npx tsx tableaux/tab-01.ts

Résultat de l'exécution :

1
2
3
tab1===tab2 : true
tab1= [ 1, 10, 3 ]
tab2= [ 1, 10, 3 ]
  • tab1===tab2 vaut true : les deux variables pointent vers le même tableau en mémoire ;
  • modifier le tableau via tab2 modifie donc aussi ce que l'on voit à travers tab1 — c'est la conséquence directe du fait que tab1 et tab2 désignent une seule et même donnée.

4.2. script [tab-02]

Ce script montre que le tableau TypeScript n'a pas de taille fixe, et se comporte plus comme une liste indexée dynamique que comme un tableau classique de langage compilé :

'use strict';

// tableau
// [any] car ce tableau va contenir des trous et des valeurs de types différents
const tab: any[] = [];
console.log("tab=", tab, ", longueur=[", tab.length, "]");
console.log("-------------------------------");
// initialisation d'un élément
tab[3] = 100;
tab[1] = "huit";
// tableau
console.log("tab=", tab, ", longueur=[", tab.length, "]");
console.log("-------------------------------");
// toString
console.log("tab.toString=[", tab.toString(), "]");
console.log("-------------------------------");
// les clés du tableau sont ses indices
for (let key of tab.keys()) {
  console.log("clé=[", key, "], valeur=[", tab[key], "]");
}
console.log("-------------------------------");
// les valeurs du tableau
for (let value of tab.values()) {
  console.log("valeur=[", value, "]");
}
  • ligne 5 : const tab: any[] = [] — un tableau vide, typé any[] car il va contenir volontairement des « trous » ;
  • lignes 9-10 : on peut initialiser l'élément n°3 sans que les éléments 0, 1, 2 existent déjà — ils deviennent des « trous » (empty item), différents de undefined bien qu'ils s'affichent souvent de façon proche ;
  • lignes 18-20 : les méthodes keys() et values() rendent des itérateurs, parcourables avec for...of.
npx tsx tableaux/tab-02.ts

Résultat de l'exécution :

tab= [] , longueur=[ 0 ]
-------------------------------
tab= [ <1 empty item>, 'huit', <1 empty item>, 100 ] , longueur=[ 4 ]
-------------------------------
tab.toString=[ ,huit,,100 ]
-------------------------------
clé=[ 0 ], valeur=[ undefined ]
clé=[ 1 ], valeur=[ huit ]
clé=[ 2 ], valeur=[ undefined ]
clé=[ 3 ], valeur=[ 100 ]
-------------------------------
valeur=[ undefined ]
valeur=[ huit ]
valeur=[ undefined ]
valeur=[ 100 ]

4.3. script [tab-03]

Ce script présente les principales méthodes de manipulation d'un tableau : parcours, ajout, suppression d'éléments.

'use strict';
// un tableau peut contenir différents types de données
// [any] car ce tableau mélange volontairement plusieurs types (démonstration)
const tab: any[] = [1, 2, "un", "deux", true, [10, 20], { prop1: 10, prop2: "abc" }];
// console.log sait afficher le contenu d'un tableau
show(1);
console.log("tab=", tab);
show(2);
// parcours du tableau avec foreach
tab.forEach(element => {
  console.log("élément=", element, typeof (element));
});
show("2b");
// une autre écriture pour faire la même chose
tab.forEach(function (element) {
  console.log("élément=", element, typeof (element));
});
show(3);
// parcours du tableau avec for
for (let i = 0; i < tab.length; i++) {
  console.log("i=", i, "tab[i]=", tab[i]);
}
show(4);
// modification tab[i]
tab[5] = [];
// affichage
console.log("tab=", tab);
show(5);
// on enlève le dernier élément du tableau
let element = tab.pop();
console.log("élément=", element, "tab=", tab);
show(6);
// on ajoute un élément à la fin du tableau
tab.push('xyz');
console.log("tab=", tab);
show(7);
// on ajoute un élément au début du tableau
tab.unshift(1000);
console.log("tab=", tab);
show(8);
// on enlève le 1er élément
element = tab.shift();
console.log("élément=", element, "tab=", tab);
show(9);
// on enlève l'élément n° 2 du tableau
element = tab.splice(2, 1);
console.log("élément=", element, "tab=", tab);
show(10);
// on enlève du tableau deux éléments à partir de l'élément n° 1
element = tab.splice(1, 2);
console.log("élément=", element, "tab=", tab);

// fonction
function show(param: number | string): void {
  console.log("[", param, ":::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ]");
}
  • ligne 4 : bien que tab soit déclaré const, on peut modifier son contenu (ligne 25, tab[5] = []). C'est le pointeur vers le tableau qui est constant, pas le tableau lui-même ;
  • lignes 10-12 : forEach parcourt le tableau et appelle, pour chaque élément, la fonction passée en paramètre — ici écrite avec la notation fléchée element => { ... } ;
  • lignes 15-17 : la même chose, écrite avec la syntaxe function traditionnelle — les deux formes sont équivalentes ;
  • les méthodes pop(), push(), unshift(), shift(), splice() modifient le tableau en place (elles ont un effet de bord) : c'est une différence importante avec les méthodes « immuables » vues au script tab-05.
npx tsx tableaux/tab-03.ts

Résultat de l'exécution (mis en forme sur une ligne par souci de lisibilité) :

[ 1 :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ]
tab= [ 1, 2, 'un', 'deux', true, [ 10, 20 ], { prop1: 10, prop2: 'abc' } ]
[ 2 :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ]
élément= 1 number
élément= 2 number
élément= un string
élément= deux string
élément= true boolean
élément= [ 10, 20 ] object
élément= { prop1: 10, prop2: 'abc' } object
[ 2b :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ]
élément= 1 number
élément= 2 number
élément= un string
élément= deux string
élément= true boolean
élément= [ 10, 20 ] object
élément= { prop1: 10, prop2: 'abc' } object
[ 3 :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ]
i= 0 tab[i]= 1
i= 1 tab[i]= 2
i= 2 tab[i]= un
i= 3 tab[i]= deux
i= 4 tab[i]= true
i= 5 tab[i]= [ 10, 20 ]
i= 6 tab[i]= { prop1: 10, prop2: 'abc' }
[ 4 :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ]
tab= [ 1, 2, 'un', 'deux', true, [], { prop1: 10, prop2: 'abc' } ]
[ 5 :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ]
élément= { prop1: 10, prop2: 'abc' } tab= [ 1, 2, 'un', 'deux', true, [] ]
[ 6 :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ]
tab= [ 1, 2, 'un', 'deux', true, [], 'xyz' ]
[ 7 :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ]
tab= [ 1000, 1, 2, 'un', 'deux', true, [], 'xyz' ]
[ 8 :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ]
élément= 1000 tab= [ 1, 2, 'un', 'deux', true, [], 'xyz' ]
[ 9 :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ]
élément= [ 'un' ] tab= [ 1, 2, 'deux', true, [], 'xyz' ]
[ 10 :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ]
élément= [ 2, 'deux' ] tab= [ 1, true, [], 'xyz' ]

4.4. script [tab-04]

Ce script présente les méthodes fonctionnelles de transformation d'un tableau : map, reduce, filter, find, findIndex, indexOf, sort.

'use strict';

// méthode de manipulation de tableaux

// un tableau
const tab: number[] = [];
for (let i = 0; i < 10; i++) {
  tab[i] = i * 10;
}
// affichage
console.log("tab=", tab);
// map
const tab2 = tab.map(element => {
  return { prop1: element, prop2: element * element }
});
// affichage
console.log("tab=", tab);
console.log("tab2=", tab2);
// reduce sans valeur initiale
const somme = tab.reduce((accumulator, currentValue) => accumulator + currentValue);
console.log("somme tab=", somme);
// reduce avec valeur initiale 10
const somme2 = tab.reduce((accumulator, currentValue) => accumulator + currentValue, 10);
console.log("somme2 tab=", somme2);
// filter
const tab4 = tab.filter((element) => {

    return element > 50;

});
console.log("tab4=", tab4);
// find
const element1 = tab.find((element) => (element > 20));
console.log("élément1=", element1);
// findIndex
const index1 = tab.findIndex((element) => (element === 20));
console.log("index1 20=", index1);
// indexOf
const index2 = tab.indexOf(30);
console.log("index2 30=", index2);
const index3 = tab.indexOf(31);
console.log("index3 31=", index3);
// lastIndexOf
const index4 = [4, 5, 4, 2].lastIndexOf(4);
console.log("index4 4=", index4);
// sort
const tab5 = [4, 5, 4, 2].sort();
console.log("tab5=", tab5);
// sort inverse
const tab6 = [4, 5, 4, 2].sort((e1, e2) => {
  if (e1 > e2) {
    return -1;
  }
  else if (e1 === e2) {
    return 0;
  } else {
    return +1;
  }
});
console.log("tab6=", tab6);
  • [map] transforme chaque élément et rend un nouveau tableau — l'original n'est pas modifié ;
  • [reduce] « accumule » les éléments un par un dans un résultat unique. Le 1er paramètre de sa fonction (accumulator) est le résultat accumulé jusqu'ici ; le 2nd (currentValue), l'élément courant. Sans valeur initiale (ligne 20), l'accumulateur démarre avec le premier élément du tableau ;
  • [filter] garde uniquement les éléments pour lesquels la fonction rend une valeur « vraie » ;
  • [find] rend le premier élément satisfaisant le critère (ou undefined), [findIndex] rend son index (ou -1) ;
  • [sort] sans paramètre trie dans l'ordre naturel. Avec une fonction de comparaison (e1, e2) => ..., on maîtrise totalement l'ordre : -1 si e1 doit précéder e2, +1 sinon, 0 en cas d'égalité.
npx tsx tableaux/tab-04.ts

Résultat de l'exécution :

tab= [ 0, 10, 20, 30, 40, 50, 60, 70, 80, 90 ]
tab= [ 0, 10, 20, 30, 40, 50, 60, 70, 80, 90 ]
tab2= [
  { prop1: 0, prop2: 0 },
  { prop1: 10, prop2: 100 },
  { prop1: 20, prop2: 400 },
  { prop1: 30, prop2: 900 },
  { prop1: 40, prop2: 1600 },
  { prop1: 50, prop2: 2500 },
  { prop1: 60, prop2: 3600 },
  { prop1: 70, prop2: 4900 },
  { prop1: 80, prop2: 6400 },
  { prop1: 90, prop2: 8100 }
]
somme tab= 450
somme2 tab= 460
tab4= [ 60, 70, 80, 90 ]
élément1= 30
index1 20= 2
index2 30= 3
index3 31= -1
index4 4= 2
tab5= [ 2, 4, 4, 5 ]
tab6= [ 5, 4, 4, 2 ]

4.5. script [tab-05]

[NOUVEAU depuis 2019] Ce script présente les nouvelles méthodes des tableaux, apparues entre 2022 et 2023 :

'use strict';
// ========================================================================
// [NOUVEAU depuis 2019] nouvelles méthodes des tableaux
// ========================================================================

// ------------------------------------------------------------------------
// 1) at(index)  (ECMAScript 2022)
// ------------------------------------------------------------------------
// avant, pour accéder au dernier élément d'un tableau, on écrivait tab[tab.length - 1]
const tab: number[] = [10, 20, 30, 40, 50];
console.log("tab[tab.length - 1] =", tab[tab.length - 1]); // ancienne façon
console.log("tab.at(-1) =", tab.at(-1)); // -> dernier élément, plus lisible
console.log("tab.at(-2) =", tab.at(-2)); // -> avant-dernier élément
console.log("tab.at(0)  =", tab.at(0));  // -> équivalent à tab[0]
// .at fonctionne aussi sur les chaînes de caractères
console.log("'bonjour'.at(-1) =", "bonjour".at(-1));

// ------------------------------------------------------------------------
// 2) findLast() et findLastIndex()  (ECMAScript 2023)
// ------------------------------------------------------------------------
// symétriques de find() / findIndex()
const nombres: number[] = [5, 12, 8, 21, 3, 17];
const dernierPair = nombres.findLast(n => n % 2 === 0);
console.log("dernier nombre pair =", dernierPair); // -> 8 
const indexDernierPair = nombres.findLastIndex(n => n % 2 === 0);
console.log("index du dernier nombre pair =", indexDernierPair);

// ------------------------------------------------------------------------
// 3) les méthodes "immuables"  (ECMAScript 2023)
// ------------------------------------------------------------------------
// sort(), reverse(), splice() modifient le tableau original (effet de bord) - cf tab-04.js
// leurs équivalents "to..." rendent un NOUVEAU tableau et laissent l'original intact
// [(number | string)[]] car toSpliced() insère plus bas des chaînes dans ce tableau
const original: (number | string)[] = [4, 5, 4, 2];

// toSorted : comme sort(), mais sans modifier l'original
const trié = original.toSorted();
console.log("original =", original, " (inchangé)");
console.log("trié =", trié);

// toReversed : comme reverse(), mais sans modifier l'original
const inversé = original.toReversed();
console.log("inversé =", inversé, ", original toujours =", original);

// toSpliced : comme splice(), mais sans modifier l'original
// toSpliced(indexDépart, nbÀSupprimer, ...éléments à insérer)
const modifié: (number | string)[] = original.toSpliced(1, 2, "a", "b", "c");
console.log("modifié =", modifié, ", original toujours =", original);

// with(index, valeur) : rend une copie du tableau avec un seul élément remplacé
const remplacé = original.with(0, 999);
console.log("remplacé =", remplacé, ", original toujours =", original);

// ------------------------------------------------------------------------
// pourquoi c'est utile : ces méthodes évitent les effets de bord, ce qui est
// particulièrement apprécié dans les frameworks front (React, Angular, Vue...)
// où l'on doit souvent produire un nouvel état sans modifier l'ancien.
// ------------------------------------------------------------------------
  • [at(index)] (ES2022) : accès sécurisé et lisible, y compris avec des index négatifs (at(-1) = dernier élément) — plus lisible que tab[tab.length - 1] ;
  • [findLast]/[findLastIndex] (ES2023) : comme find/findIndex, mais donne le dernier élément satisfaisant la condition ;
  • [toSorted], [toReversed], [toSpliced], [with] (ES2023) : les équivalents immuables de sort, reverse, splice et de l'affectation indexée — ils rendent un nouveau tableau sans modifier l'original, contrairement aux méthodes du script tab-03. Très utile dans les frameworks front (React, Vue...) où l'on doit produire un nouvel état sans modifier l'ancien.
npx tsx tableaux/tab-05.ts

Résultat de l'exécution :

tab[tab.length - 1] = 50
tab.at(-1) = 50
tab.at(-2) = 40
tab.at(0)  = 10
'bonjour'.at(-1) = r
dernier nombre pair = 8
index du dernier nombre pair = 2
original = [ 4, 5, 4, 2 ]  (inchangé)
trié = [ 2, 4, 4, 5 ]
inversé = [ 2, 4, 5, 4 ] , original toujours = [ 4, 5, 4, 2 ]
modifié = [ 4, 'a', 'b', 'c', 2 ] , original toujours = [ 4, 5, 4, 2 ]
remplacé = [ 999, 5, 4, 2 ] , original toujours = [ 4, 5, 4, 2 ]

4.6. Conclusion

Retenons de ce chapitre qu'un tableau est un objet référencé par un pointeur, sans taille fixe, doté d'un riche jeu de méthodes — certaines modifiant le tableau en place (push, sort...), d'autres, plus récentes, rendant systématiquement un nouveau tableau (toSorted, with...). Le chapitre suivant aborde les objets littéraux.