Les tableaux

Dans la première partie de ce cours vous avez déjà pu vous initier de manière basique aux tableaux. Ce que vous y avez appris vous a sûrement suffi jusqu'à présent, mais il faut savoir que les tableaux possèdent de nombreuses méthodes qui vous sont encore inconnues et qui pourtant pourraient vous aider facilement à traiter leur contenu. Dans ce chapitre nous allons donc étudier de manière avancée l'utilisation des tableaux.

L'objet Array

L'objetArrayest à la base de tout tableau. Il possède toutes les méthodes et les propriétés nécessaires à l'utilisation et à la modification des tableaux. Précisons que cet objet ne concerne que les tableaux itératifs, les objets littéraux ne sont pas des tableaux, ce sont des objets, tout simplement !

Le constructeur

Cet objet peut être instancié de trois manières différentes. Cependant, gardez bien à l'esprit que l'utilisation de son type primitif est bien préférable à l'instanciation de son objet. Nous n'abordons ce sujet qu'à titre indicatif.

Instanciation sans arguments
var myArray = new Array();

Ce code génère un tableau vide.

Instanciation en spécifiant chaque valeur à attribuer
var myArray = new Array('valeur1', 'valeur2', , 'valeurX');

Ce code revient à créer un tableau de cette manière :

var myArray = ['valeur1', 'valeur2', , 'valeurX'];
Instanciation en spécifiant la longueur du tableau
var myArray = new Array(longueur_du_tableau);

Voici un cas particulier du constructeur de l'objetArray: il est possible de spécifier la longueur du tableau. Cela paraît assez intéressant sur le principe, mais en réalité cela ne sert quasiment à rien vu que le JavaScript redéfinit la taille des tableaux quand on ajoute ou supprime un item du tableau.

Les propriétés

Ici, les tableaux ont le mérite de rendre les choses simples, ils ne possèdent qu'une seule propriété (accessible uniquement après instanciation) que vous connaissez déjà tous :length! Pour rappel, cette propriété est en lecture seule et vous indique combien d'éléments existent dans votre tableau.

Ainsi, avec ce tableau :

var myArray = [
'élément1',
'élément2',
'élément3',
'élément4'
];

La propriétélengthrenverra 4.

Les méthodes

Plusieurs méthodes ont déjà été abordées au cours du chapitre de la première partie consacré aux tableaux. Elles sont de nouveau listées dans ce chapitre, mais de manière plus approfondie afin que celui-ci vous serve, en quelque sorte, de référence.

Concaténer deux tableaux

Aussi étrange que cela puisse paraître, le JavaScript ne permet pas l'utilisation de l'opérateur + pour concaténer plusieurs tableaux entre eux. Si on tente de s'en servir, on obtient alors en sortie une chaîne de caractères contenant tous les éléments des tableaux. Ainsi, l'opération suivante :

var myArray = ['test1', 'test2'] + ['test3', 'test4'];
alert(myArray);

donne la chaîne de caractères suivante :

test1,test2test3,test4

Pas terrible, n'est-ce pas ? Heureusement, les tableaux possèdent une méthode nomméeconcat()qui nous permet d'obtenir le résultat souhaité :

var myArray = ['test1', 'test2'].concat(['test3', 'test4']);
alert(myArray);

Ce code nous retourne le tableau suivant :

['test1', 'test2', 'test3', 'test4']

Parcourir un tableau

Le fait de parcourir un tableau est une façon de faire très courante en programmation, que ce soit en JavaScript ou dans un autre langage. Vous savez déjà faire ça de cette manière :

var myArray = ["C'est", "un", "test"],
length = myArray.length;
for (var i = 0; i < length; i++) {
alert(
'Index : ' + i + '\n' +
'Valeur : ' + myArray[i]
);
}

Essayer le code

Cependant, ce code est quand même contraignant, nous sommes obligés de créer deux variables, une pour l'incrémentation, et une pour stocker la longueur de notre tableau (cela évite à notre boucle d'aller chercher la longueur dans le tableau, on économise des ressources), tout ça n'est pas très pratique.

C'est là qu'intervient une nouvelle méthode nomméeforEach(). Cette méthode prend pour paramètre deux arguments, le premier reçoit la fonction à exécuter pour chaque index existant et le deuxième (qui est facultatif) reçoit un objet qui sera pointé par le mot-cléthisdans la fonction que vous avez spécifiée pour le premier argument.

Concentrons-nous sur la fonction passée en paramètre. Celle-ci sera exécutée pour chaque index existant (dans l'ordre croissant bien entendu) et recevra en paramètres trois arguments :

  • Le premier contient la valeur contenue à l'index actuel ;

  • Le deuxième contient l'index actuel ;

  • Le troisième est une référence au tableau actuellement parcouru.

Essayons donc :

var myArray = ["C'est", "un", "test"];
myArray.forEach(function(value, index, array) {
alert(
'Index : ' + index + '\n' +
'Valeur : ' + value
);
});

Essayer le code

Vous avez sûrement constaté que nous n'utilisons pas l'argumentarraydans notre fonction anonyme, vous pouvez très bien ne pas le spécifier, votre code fonctionnera sans problème !

Rechercher un élément dans un tableau

Tout comme les chaînes de caractères, les tableaux possèdent aussi les fonctionsindexOf()etlastIndexOf(). Elles fonctionnent de la même manière, sauf qu'au lieu de ne chercher qu'une chaîne de caractères vous pouvez faire une recherche pour n'importe quel type de valeur, que ce soit une chaîne de caractères, un nombre ou un objet. La valeur retournée par la fonction est l'index du tableau dans lequel se trouve votre élément recherché, en cas d'échec la fonction vous retourne toujours la valeur -1.

Prenons un exemple :

var element2 = ['test'],
myArray = ['test', element2];
alert(myArray.indexOf(element2)); // Affiche : 1

Dans ce code, c'est bien le tableau['test']qui a été trouvé, et non pas la chaîne de caractères'test'!

Pourquoi avoir créé la variableelement2?

Ah, en fait il y a une logique bien simple à cela :

alert(['test'] == ['test']); // Affiche : « false »

Les deux tableaux sont de même valeur mais sont pourtant reconnus comme étant deux tableaux différents, tout simplement parce que ce ne sont pas les mêmes instanciations de tableaux ! Lorsque vous écrivez une première fois['test'], vous faites une première instanciation de tableau, donc la deuxième fois que vous écrirez cela vous ferez une deuxième instanciation.

La solution pour être sûr de comparer deux mêmes instanciations est de passer la référence de votre instanciation à une variable. Ainsi, vous n'avez plus aucun problème :

var myArray = ['test'];
alert(myArray == myArray); // Affiche : « true »

Pour terminer sur nos deux fonctions, sachez qu'elles possèdent, elles aussi, un second paramètre permettant de spécifier à partir de quel index vous souhaitez faire débuter la recherche. 

Trier un tableau

Deux méthodes peuvent vous servir à trier un tableau. Nous allons commencer par la plus simple d'entre elles :reverse().

La méthodereverse()

Cette méthode ne prend aucun argument en paramètre et ne retourne aucune valeur, son seul rôle est d'inverser l'ordre des valeurs de votre tableau :

var myArray = [1, 2, 3, 4, 5];
myArray.reverse();
alert(myArray); // Affiche : 5,4,3,2,1

Plutôt simple, non ?

La méthodesort()

En ce qui concerne la deuxième méthode, les choses se corsent un peu. Celle-ci se nommesort(), par défaut cette méthode trie votre tableau par ordre alphabétique uniquement. Mais cette méthode possède aussi un argument facultatif permettant de spécifier l'ordre à définir, et c'est là que les choses se compliquent. Tout d'abord, prenons un exemple simple :

var myArray = [3, 1, 5, 10, 4, 2];
myArray.sort();
alert(myArray); // Affiche : 1,10,2,3,4,5

Quand nous disions que cette méthode ne triait, par défaut, que par ordre alphabétique, c'était vrai et ce dans tous les cas ! Cette méthode possède en fait un mode de fonctionnement bien particulier : elle commence par convertir toutes les données du tableau en chaînes de caractères et ce n'est qu'après ça qu'elle applique son tri alphabétique. Dans notre exemple, la logique peut vous paraître obscure, mais si nous essayons de remplacer nos chiffres par des caractères cela devrait vous paraître plus logique :

0 = a ; 1 = b ; 2 = c

Notre suite « 1, 10, 2 » devient donc « b, ba, c » ! Ce tri vous paraît déjà plus logique avec des caractères, non ? Eh bien, pour la méthodesort(), cette logique s'applique même aux chiffres !

Venons-en maintenant à l'argument facultatif desort(): il a pour but de réaliser un tri personnalisé. Il doit contenir une référence vers une fonction que vous avez créée, cette dernière devant posséder deux arguments qui seront spécifiés par la méthodesort(). La fonction devra alors dire si les valeurs transmises en paramètres sont de même valeur, ou bien si l'une des deux est supérieure à l'autre.

Notre but ici est de faire en sorte que notre tri soit, non pas alphabétique, mais par ordre croissant (et donc que la valeur 10 se retrouve à la fin du tableau). Nous allons donc commencer par créer notre fonction anonyme que nous fournirons au moment du tri :

function(a, b) {
// Comparaison des valeurs
}

Nous avons notre fonction, mais que faire maintenant ? Eh bien, nous allons devoir comparer les deux valeurs fournies. Avant tout, sachez que la méthodesort()ne convertit pas les données du tableau en chaînes de caractères lorsque vous avez défini l'argument facultatif, ce qui fait que les valeurs que nous allons recevoir en paramètres seront bien de typeNumberet non pas de typeString, cela nous facilite déjà la tâche !

Commençons par écrire le code pour comparer les valeurs :

function(a, b) {
if (a < b) {
// La valeur de a est inférieure à celle de b
} else if (a > b) {
// La valeur de a est supérieure à celle de b
} else {
// Les deux valeurs sont égales
}
}

Bien, nous avons fait nos comparaisons, mais que faut-il renvoyer à la méthodesort()pour lui indiquer qu'une valeur est inférieure, supérieure ou égale à l'autre ?

Le principe est simple :

  • On retourne -1 lorsqueaest inférieur àb;

  • On retourne 1 lorsqueaest supérieur àb;

  • Et on retourne 0 quand les valeurs sont égales.

Notre fonction devient donc la suivante :

function(a, b) {
if (a < b) {
return -1;
} else if (a > b) {
return 1;
} else {
return 0;
}
}

Essayons donc le code complet maintenant :

var myArray = [3, 1, 5, 10, 4, 2];
myArray.sort(function(a, b) {
if (a < b) {
return -1;
} else if (a > b) {
return 1;
} else {
return 0;
}
});
alert(myArray); // Affiche : 1,2,3,4,5,10

Et voilà ! La méthodesort()trie maintenant notre tableau dans l'ordre croissant !

Extraire une partie d'un tableau

Il se peut que vous ayez besoin un jour ou l'autre d'extraire une partie d'un tableau : la méthodeslice()est là pour ça. Elle prend en paramètre deux arguments, dont le deuxième est facultatif. Le premier est l'index (inclus) à partir duquel vous souhaitez commencer l'extraction du tableau, le deuxième est l'index (non inclus) auquel l'extraction doit se terminer. S'il n'est pas spécifié, alors l'extraction continue jusqu'à la fin du tableau.

var myArray = [1, 2, 3, 4, 5];
alert(myArray.slice(1, 3)); // Affiche : 2,3
alert(myArray.slice(2)); // Affiche : 3,4,5

Notons aussi que le deuxième argument possède une petite particularité intéressante qui rappellera un peu le PHP aux connaisseurs :

var myArray = [1, 2, 3, 4, 5];
alert(myArray.slice(1, -1)); // Affiche : 2,3,4

Lorsque vous spécifiez un nombre négatif au deuxième argument, alors l'extraction se terminera à l'index de fin moins la valeur que vous avez spécifiée. Dans notre exemple, l'extraction se termine donc à l'index qui précède celui de la fin du tableau, donc à l'index 3.

Remplacer une partie d'un tableau

Nous allons aborder ici l'utilisation d'une méthode assez peu utilisée en raison de son usage assez particulier, il s'agit desplice(). Cette méthode reçoit deux arguments obligatoires, puis une infinité d'arguments facultatifs. Le premier argument est l'index à partir duquel vous souhaitez effectuer vos opérations, le deuxième est le nombre d'éléments que vous souhaitez supprimer à partir de cet index. Exemple :

var myArray = [1, 2, 3, 4, 5];
var result = myArray.splice(1, 2); // On retire 2 éléments à partir de l'index 1
alert(myArray); // Affiche : 1,4,5
alert(result); // Affiche : 2,3

À partir de ce code, vous devriez pouvoir faire deux constatations :

  • La méthodesplice()modifie directement le tableau à partir duquel elle a été exécutée ;

  • Elle renvoie un tableau des éléments qui ont été supprimés.

Continuons sur notre lancée ! Les arguments qui suivent les deux premiers contiennent les éléments qui doivent être ajoutés en remplacement de ceux effacés. Vous pouvez très bien spécifier plus d'éléments à ajouter que d'éléments qui ont été supprimés, ce n'est pas un problème. Essayons donc l'ajout d'éléments :

var myArray = [1, null, 4, 5];
myArray.splice(1, 1, 2, 3);
alert(myArray); // Affiche : 1,2,3,4,5

Notez bien aussi une chose : si vous ajoutez des éléments dans le tableau, vous pouvez mettre le deuxième argument à 0, ce qui aura pour effet d'ajouter des éléments sans être obligé d'en supprimer d'autres. Cette méthodesplice()peut donc être utilisée comme une méthode d'insertion de données.

Tester l'existence d'un tableau

Pour terminer sur les méthodes des tableaux, sachez que les tableaux possèdent une méthode propre à l'objet constructeur nomméeisArray(). Comme son nom l'indique, elle permet de tester si la variable passée en paramètre contient un tableau. Son utilisation est ultra-simple :

alert(Array.isArray(['test']));

Les piles et les files

Nous allons ici aborder un concept que vous avez déjà rapidement étudié dans ce cours, mais qu'il serait bon de vous remettre en tête.

Les piles et les files sont deux manières de manipuler vos tableaux. Plutôt que de les voir comme de simples listes de données, vous pouvez les imaginer comme étant, par exemple, une pile de livres où le dernier posé sera au final le premier récupéré, ou bien comme une file d'attente, où le dernier entré sera le dernier sorti. Ces deux façons de faire sont bien souvent très pratiques dans de nombreux cas, vous vous en rendrez bien vite compte.

Retour sur les méthodes étudiées

Quatre méthodes ont été étudiées au cours des premiers chapitres de ce cours. Il est de bon ton de revenir sur leur utilisation avant d'entamer le sujet des piles et des files :

  • push(): ajoute un ou plusieurs éléments à la fin du tableau (un argument par élément ajouté) et retourne la nouvelle taille de ce dernier.

  • pop(): retire et retourne le dernier élément d'un tableau.

  • unshift(): ajoute un ou plusieurs éléments au début du tableau (un argument par élément ajouté) et retourne la nouvelle taille de ce dernier.

  • shift(): retire et retourne le premier élément d'un tableau.

Les piles

Les piles partent du principe que le premier élément ajouté sera le dernier retiré, comme une pile de livres ! Elles sont utilisables de deux manières différentes : soit avec les deux méthodespush()etpop(), soit avec les deux restantesunshift()etshift(). Dans le premier cas, la pile sera empilée et dépilée à la fin du tableau, dans le deuxième cas, les opérations se feront au début du tableau.

var myArray = ['Livre 1'];
var result = myArray.push('Livre 2', 'Livre 3');
alert(myArray); // Affiche : « Livre 1,Livre 2,Livre 3 »
alert(result); // Affiche : « 3 »
result = myArray.pop();
alert(myArray); // Affiche : « Livre 1,Livre 2 »
alert(result); // Affiche : « Livre 3 »

Aucun problème pour les méthodespush()etpop()? Essayons maintenant le coupleunshift()/shift():

var myArray = ['Livre 3'];
var result = myArray.unshift('Livre 1', 'Livre 2');
alert(myArray); // Affiche : « Livre 1,Livre 2,Livre 3 »
alert(result); // Affiche : « 3 »
result = myArray.shift();
alert(myArray); // Affiche : « Livre 2,Livre 3 »
alert(result); // Affiche : « Livre 1 »

Voilà pour les piles !

Les files

Les files partent d'un autre principe tout aussi simple : le premier élément ajouté est le premier sorti, comme une file d'attente. Elles sont, elles aussi, utilisables de deux manières différentes : soit avec le couplepush()/shift(), soit avec le coupleunshift()/pop().

var myArray = ['Fanboy 1', 'Fanboy 2'];
var result = myArray.push('Fanboy 3', 'Fanboy 4');
alert(myArray); // Affiche : « Fanboy 1,Fanboy 2,Fanboy 3,Fanboy 4 »
alert(result); // Affiche : « 4 »
result = myArray.shift();
alert(myArray); // Affiche : « Fanboy 2,Fanboy 3,Fanboy 4 »
alert(result); // Affiche : « Fanboy 1 »

Le coupleunshift()/pop()est tout aussi simple d'utilisation :

var myArray = ['Fanboy 3', 'Fanboy 4'];
var result = myArray.unshift('Fanboy 1', 'Fanboy 2');
alert(myArray); // Affiche : « Fanboy 1,Fanboy 2,Fanboy 3,Fanboy 4 »
alert(result); // Affiche : « 4 »
result = myArray.pop();
alert(myArray); // Affiche : « Fanboy 1,Fanboy 2,Fanboy 3 »
alert(result); // Affiche : « Fanboy 4 »

Voilà pour les files !

Quand les performances sont absentes :unshift()etshift()

Revenons maintenant sur ce petit problème de performances. Les deux méthodesunshift()etshift()utilisent chacune un algorithme qui fait qu'en retirant ou en ajoutant un élément en début de tableau, elles vont devoir réécrire tous les index des éléments qui suivent. En gros, prenons un tableau de ce style :

0 => 'test 1'
1 => 'test 2'
2 => 'test 3'

En ajoutant un élément en début de tableau, nous cassons l'indexation :

0 => 'test supplémentaire'
0 => 'test 1'
1 => 'test 2'
2 => 'test 3'

Ce qui fait que nous devons réécrire tous les index suivants :

0 => 'test supplémentaire'
1 => 'test 1'
2 => 'test 2'
3 => 'test 3'

Si le tableau possède de nombreux éléments, cela peut parfois prendre un peu de temps. C'est ce qui fait que les piles sont généralement préférées aux files en JavaScript, car elles peuvent se passer de ces deux méthodes. Cela dit, il faut relativiser : la perte de performance n'est pas dramatique, vous pouvez très bien vous en servir pour des tableaux de petite taille (en dessous de 10 000 entrées, en gros), mais au-dessus il faudra peut-être songer à utiliser les piles ou bien à utiliser des scripts qui résolvent ce genre de problèmes. ;)

En résumé
  • Pour concaténer deux tableaux, il faut utiliser la méthodeconcat(), car l'opérateur + ne fonctionne pas selon le comportement voulu.

  • La méthodeforEach()permet de parcourir un tableau en s'affranchissant d'une bouclefor

  • indexOf()etlastIndexOf()permettent de rechercher un élément qui peut être une chaîne de caractères, un nombre, ou même un tableau. Il faudra toutefois faire attention lors de la comparaison de deux tableaux.

  • L'utilisation d'une fonction pour trier un tableau est possible et se révèle particulièrement utile pour effectuer un tri personnalisé.

  • Les piles et les files sont un moyen efficace pour stocker et accéder à de grandes quantités de données.