Les tableaux
Ce chapitre présente les Array (tableaux), la structure de données la plus utilisée en Ruby. Un Array permet de stocker plusieurs valeurs dans une seule variable.
Principe
Jusqu'ici, chaque variable ne contenait qu'une seule valeur : un nom, un niveau, un type. Mais comment stocker une équipe de 6 Pokémon ? Créer 6 variables séparées (pokemon_1, pokemon_2...) serait fastidieux. C'est le rôle de l'Array : une liste ordonnée d'éléments, accessible par leur position.
On peut voir un Array comme un casier à plusieurs compartiments numérotés. Chaque compartiment contient une valeur, et on accède à chaque valeur par son numéro.
Créer un Array
La façon la plus simple de créer un Array est d'utiliser les crochets [] :
team = ['Pikachu', 'Dracaufeu', 'Tortank']
types = [:electric, :fire, :water]
levels = [25, 36, 40]
empty = []
p team # => ["Pikachu", "Dracaufeu", "Tortank"]
p empty # => []
- Un Array peut contenir n'importe quel type de valeur : des String, des Integer, des Symbol, et même un mélange de types différents.
[]crée un Array vide.
On peut aussi créer un Array d'une taille donnée avec Array.new :
# Créer un Array de 6 cases, toutes remplies avec nil
slots = Array.new(6, nil)
p slots # => [nil, nil, nil, nil, nil, nil]
# Créer un Array de 6 cases avec des valeurs calculées
levels = Array.new(6) { |index| (index + 1) * 10 }
p levels # => [10, 20, 30, 40, 50, 60]
Array.new(6, nil)crée 6 cases remplies avecnil.- La seconde forme utilise un bloc
{ |index| ... }qui calcule la valeur de chaque case. On n'a pas encore vu les blocs en détail, mais la syntaxe est simple : entre{ }, le|index|reçoit le numéro de la case (en partant de 0), et l'expression après produit la valeur.
Accéder aux éléments
Chaque élément a un index (sa position dans l'Array). Attention : les index commencent à 0, pas à 1 :
team[0]accède au premier élément. C'est une convention de la programmation : on commence à compter à partir de 0.- Les index négatifs comptent depuis la fin.
-1est le dernier,-2l'avant-dernier.
Ruby offre aussi des raccourcis :
team = ['Pikachu', 'Dracaufeu', 'Tortank']
puts team.first # => Pikachu
puts team.last # => Tortank
Modifier un élément
On peut remplacer un élément en assignant une nouvelle valeur à un index :
team = ['Pikachu', 'Dracaufeu', 'Tortank']
puts team[0] # => Pikachu
team[0] = 'Raichu'
puts team[0] # => Raichu
p team # => ["Raichu", "Dracaufeu", "Tortank"]
Connaître la taille
team = ['Pikachu', 'Dracaufeu', 'Tortank']
puts team.size # => 3
puts team.length # => 3 (identique à .size)
puts team.empty? # => false
puts [].empty? # => true
.sizeet.lengthfont la même chose..empty?retournetruesi l'Array ne contient aucun élément.
Ajouter des éléments
team = ['Pikachu']
# Ajouter à la fin
team.push('Dracaufeu')
team << 'Tortank' # raccourci, le plus utilisé
p team # => ["Pikachu", "Dracaufeu", "Tortank"]
# Ajouter au début
team.unshift('Mew')
p team # => ["Mew", "Pikachu", "Dracaufeu", "Tortank"]
# Insérer à une position précise
team.insert(2, 'Ectoplasma')
p team # => ["Mew", "Pikachu", "Ectoplasma", "Dracaufeu", "Tortank"]
<<est la forme la plus courante pour ajouter à la fin. On le lit "ajouter" ou "pousser"..unshiftajoute au début..insert(position, valeur)insère à un index donné.
Supprimer des éléments
team = ['Pikachu', 'Dracaufeu', 'Tortank', 'Ectoplasma']
# Retirer le dernier
last_removed = team.pop
puts last_removed # => Ectoplasma
p team # => ["Pikachu", "Dracaufeu", "Tortank"]
# Retirer le premier
first_removed = team.shift
puts first_removed # => Pikachu
p team # => ["Dracaufeu", "Tortank"]
# Retirer par valeur (toutes les occurrences)
team = ['Pikachu', 'Dracaufeu', 'Pikachu']
team.delete('Pikachu')
p team # => ["Dracaufeu"]
# Retirer par index
team = ['Pikachu', 'Dracaufeu', 'Tortank']
team.delete_at(1)
p team # => ["Pikachu", "Tortank"]
.popet.shiftretournent l'élément retiré, ce qui permet de le stocker dans une variable..delete(valeur)supprime toutes les occurrences de cette valeur.
Chercher dans un Array
team = ['Pikachu', 'Dracaufeu', 'Tortank', 'Ectoplasma']
puts team.include?('Pikachu') # => true
puts team.include?('Mewtwo') # => false
puts team.index('Tortank') # => 2 (sa position)
puts team.index('Mewtwo') # => nil (pas trouvé)
.include?vérifie si une valeur est dans l'Array..indexretourne sa position, ounilsi elle n'y est pas.
Parcourir un Array avec each
Pour faire quelque chose avec chaque élément d'un Array, on utilise .each :
team = ['Pikachu', 'Dracaufeu', 'Tortank']
team.each { |pokemon| puts "Go, #{pokemon} !" }
Affiche :
Go, Pikachu !
Go, Dracaufeu !
Go, Tortank !
.eachpasse chaque élément, un par un, au bloc{ |pokemon| ... }. Le motpokemonentre les barres| |est le nom qu'on donne à l'élément courant. On peut choisir n'importe quel nom, mais il est plus lisible de prendre le singulier de la collection.- En Ruby, beaucoup de méthodes d'Array acceptent un bloc pour personnaliser leur comportement. Dans ce chapitre, certaines méthodes sont montrées sans bloc (comme
.sort,.reverse), mais elles ont souvent une variante avec bloc qu'on découvrira plus tard. Quand on voit une méthode d'Array, il y a de bonnes chances qu'on puisse lui passer un bloc. - Pour un bloc qui tient sur une ligne, on utilise
{ }. Pour un bloc plus long, on utilisedo ... end:
team.each do |pokemon|
puts "Pokémon : #{pokemon}"
puts " Longueur du nom : #{pokemon.length}"
end
Parcourir avec l'index
Quand on a besoin de la position de chaque élément en plus de sa valeur :
team = ['Pikachu', 'Dracaufeu', 'Tortank']
team.each_with_index do |pokemon, index|
puts "#{index + 1}. #{pokemon}"
end
Affiche :
1. Pikachu
2. Dracaufeu
3. Tortank
.each_with_indexpasse deux valeurs au bloc : l'élément et son index. On ajoute 1 à l'index pour un affichage plus naturel (commencer à 1 plutôt qu'à 0).
Transformer un Array avec map
.map crée un nouvel Array en transformant chaque élément :
team = ['Pikachu', 'Dracaufeu', 'Tortank']
uppercased_team = team.map { |pokemon| pokemon.upcase }
p uppercased_team # => ["PIKACHU", "DRACAUFEU", "TORTANK"]
p team # => ["Pikachu", "Dracaufeu", "Tortank"] (inchangé)
.mapne modifie pas l'Array original. Il retourne un nouvel Array avec les résultats.
Filtrer un Array avec select et reject
.selectgarde les éléments pour lesquels le bloc retournetrue..rejectfait l'inverse : il exclut ceux pour lesquels le bloc retournetrue.
Trier et réorganiser
team = ['Tortank', 'Pikachu', 'Dracaufeu']
p team.sort # => ["Dracaufeu", "Pikachu", "Tortank"] (ordre alphabétique)
p team.reverse # => ["Dracaufeu", "Pikachu", "Tortank"] (ordre inversé)
levels = [36, 25, 40, 10]
p levels.sort # => [10, 25, 36, 40] (ordre croissant)
.sorttrie par ordre alphabétique (String) ou numérique (Integer)..reverseinverse l'ordre..sortpeut aussi prendre un bloc pour trier selon un critère personnalisé (par exemple trier par longueur de nom). On verra cette forme dans les chapitres suivants.- Comme
.mapet.select, ces méthodes retournent un nouvel Array sans modifier l'original.
Autres méthodes utiles
# Supprimer les nil
sparse = ['Pikachu', nil, 'Tortank', nil]
p sparse.compact # => ["Pikachu", "Tortank"]
# Supprimer les doublons
duplicates = ['Pikachu', 'Dracaufeu', 'Pikachu', 'Tortank']
p duplicates.uniq # => ["Pikachu", "Dracaufeu", "Tortank"]
# Convertir un Array en String
team = ['Pikachu', 'Dracaufeu', 'Tortank']
puts team.join(', ') # => Pikachu, Dracaufeu, Tortank
puts team.join(' | ') # => Pikachu | Dracaufeu | Tortank
.compactsupprime tous lesnil..uniqsupprime les doublons..joinest l'inverse de.split(vu au chapitre 2) : il combine les éléments en une seule String avec un séparateur.
Conclusion
- Un Array est une liste ordonnée d'éléments, accessible par index (qui commence à 0).
<<est la façon idiomatique d'ajouter un élément à la fin..eachparcourt chaque élément..each_with_indexdonne aussi la position..maptransforme chaque élément et retourne un nouvel Array..selectgarde les éléments qui correspondent à une condition..rejectfait l'inverse..sorttrie,.reverseinverse,.compactsupprime les nil,.uniqsupprime les doublons..include?vérifie la présence d'un élément..indexretourne sa position..joinconvertit en String..split(vu au chapitre 2) fait l'inverse.