Articles

Comprendre les méthodes énumérables #inject/#reduce de Ruby en moins de 5 minutes

Pour commencer, cette méthode prend un tableau et deux arguments lorsqu’elle est définie. Cela peut être visualisé comme suit:

.inject { |memo, value| 
#more lines of code here
}.reduce { |memo, value|
#more lines of code here
}

À noter, la variable memo ici est appelée memo pour représenter la mémoire. Cette variable est essentielle car elle stocke les données dont vous souhaitez que cette méthode se souvienne pendant qu’elle traverse le tableau, mais elle peut être nommée comme vous le souhaitez car ce n’est qu’une variable.

Je l’appellerai memo tout au long de cette explication.

memopeut recevoir une valeur de départ mais il n’est pas nécessaire d’en avoir une. Si une valeur de départ n’est pas définie, memo prend la première valeur du tableau.

### The starting value for memo is 5
.inject(5) { |memo, value|
#more lines of code here
}
### The starting value for memo is 1 (first value of the array)
.reduce { |memo, value|
#more lines of code here
}

#inject#reduce renverra toujours un seul objet de données — les données memo sont conservées à la fin. Cela étant dit, memo doit être modifié et renvoyé au fur et à mesure qu’il est en boucle, sinon il restera à la même valeur.

Maintenant que nous avons réduit la sémantique, je vais parcourir quelques exemples pour quand j’ai trouvé #inject#reduce utile.

Exemple 1: Utiliser #inject / #reduce pour appliquer une opération numérique

La façon la plus courante d’utiliser cette méthode est lorsqu’on lui donne un tableau d’entiers et que vous devez appliquer une opération numérique sur l’ensemble du tableau. Dans cet exemple, je vais montrer comment trouver la somme d’un tableau entier.

#inject#reduce, sera illustré comme tel:

.reduce { |memo, value|
memo += value
} ## The following is to illustrate how this method was traversed
~> 1 += 2 #memo returns 3
~> 3 += 3 #memo returns 6
~> 6 += 4 #memo returns 10
~> 10 += 5 #memo returns 15#=> 15

Puisque c’est une façon assez courante d’utiliser #inject#reduce, il existe une méthode abrégée que vous pouvez utiliser si vous souhaitez simplement appliquer une opération numérique dans tout votre tableau en l’indiquant de la manière suivante:

## This is for product, with no initial value stated
.inject(:*)
#=> 120 ### This is for sum, with an initial value of 5
.reduce(5, :+)
#=> 20

Exemple 2: Utiliser #inject / #reduce pour trouver la chaîne la plus longue

Pour cet exemple, disons qu’on vous donne un tableau de chaînes et que vous devez trouver la chaîne la plus longue dans ce tableau.

Dans ce cas, #inject#reduce sera affiché comme suit:

sentences = sentences.inject{ |memo, sentence| 
if memo.size < sentence.size
memo = sentence
else
memo
end
}
#=> "There are jumping lizards on the fountain"

Pour décomposer cela: Puisque nous n’initialisons pas memo avec un valeur, il va prendre le premier objet du tableau, sentences dans cet exemple. Dans notre méthode, nous avons une instruction if qui compare simplement notre longueur memo à chaque longueur sentence.

Cette instruction if est l’endroit où la magie se produit: si un sentence est plus long que celui stocké dans memo alors nous réaffectons le mémo à sentence; sinon, retournez simplement la même valeur.

De cette façon, notre valeur memo sera continuellement mise à jour avec la valeur sentence plus longue et #inject#reduce retournera que memo variable à la fin.

Si vous voulez voir chaque étape illustrée, vous pouvez l’exécuter dans Repl.it pour voir par vous-même.

sentences = sentences.inject { |memo, sentence|
puts "Memo length is: #{memo.length}\nSentence length is: #{sentence.length}"
if memo.length < sentence.length
puts "Our sentence is longer"
memo = sentence
else
puts "Our memo is longer"
memo
end
puts "Our memo is: #{memo}\n \n"
memo
}

Exemple #3: Utiliser #inject / #reduce pour trouver le nombre le plus répétitif dans un tableau

Maintenant que nous avons pratiqué l’utilisation de cette méthode de différentes manières, faisons un exemple similaire. Disons qu’on vous donne un tableau aléatoire de nombres et que vous voulez trouver quel nombre se répète le plus.

Pour ce cas, vous pouvez utiliser #inject#reduce comme ceci:

numbers = numbers.reduce { |memo, number|
if numbers.count(memo) <= numbers.count(number)
memo = number
else
memo
end
}

Maintenant, cette logique est similaire à ce que nous avons fait dans l’exemple 2, la seule différence réside dans notre instruction if, nous utilisons #count sur notre tableau initial pour trouver et stocker quelle valeur a un nombre supérieur ou égal. De cette façon, nous ne sommes renvoyés que le nombre qui a le plus grand nombre à la fin.