【Ruby】injectメソッドについて徹底解説!

Ruby

injectメソッドとは

injectメソッドとは配列等の要素を一つずつ繰り返してブロック内で要素を注入し処理をするメソッドです。

injectメソッドの構文

ruby
1
2
3
4
array = (1..10) # これで1〜10まで入れた配列になります。
array.inject(10 初期値) { |sum(初期値) && (合計値), num (注入値)| p sum += num }
# injectメソッドの返り値
=> 65

injectメソッドの出力値

shell
1
2
3
4
5
6
7
8
9
10
11
13
16
20
25
31
38
46
55
65

なぜこの様な返り値や出力値になるか、解説していきます。

ruby
1
(1..10).inject(10) { |sum, num| p sum += num }

変数arrayは1~10までの要素が入っている配列になります。

そのarrayにarray.inject(10)と実行しています。
このinjectメソッドの引数はinjectメソッドの初期値を決めています。

初期値は{ |sum, num| p sum += num } ←sum(ブロックの第一引数)に代入されます。
つまりsumに10が代入されていることになります。

では実際にsumにinjectの引数で指定した初期値が入っているか確かめます。

inject

上記の動画で、inject(10)引数10がarrayの要素の数だけ出力しているのが分かったと思います。
これでinjectに指定した引数がsumに代入されるということは分かりましたね!

それでは、次は↓が何をしているか解説します。

ruby
1
{ |sum, num| p sum += num }

先ほどsumには初期値が入ると解説しました。
なので今sumには10が入っています。

numには何が入っているかというと配列の最初の要素が入っています。

ruby
1
(1..10).inject(10) { |sum, num| p sum += num }

としているので、つまりnumには初め1が入っているわけです。
これが順次繰り返される値がnumに入っていくので、
1回目は1, 2回目は2, 3回目は3,と配列の要素がnumに入っていくわけです。

では一つ一つの値がnumに入っているか動画で確かめてみましょう。
inject

numの値が1から入っていて10まで出力されているので、ブロックの第二変数numには配列の要素が順番に入っているのが分かってもらえたと思います。

返り値10になっているのは一番最後にp numと実行しているので、一番最後に実行したものが返り値になるので、=> 10と出力されてます。

それではこれで下記のコードの意味が分かったのではないでしょうか?

ruby
1
(1..10).inject(10) { |sum, num| p sum += num }

①injectの引数10がブロック変数の第一引数sumに入ります。
②繰り返しの要素がnumに代入される。はじめ入っているのは1
③sum += num (sum = sum(10) + num(1))
④sumが11になりその結果をpメソッドで出力p sum += num => 11
⑤2回目に入る。sum += num(sum(11) + num(2)) => 13
⑥3回目、4回目..10回目と続きsumが65になり => 65と出力される。

inject

これが基本的なinjectメソッドの使い方になります。
初期値を与えて、初期値に配列の要素をどんどんinjectしていく、注入していく!ということですね。

では、もし初期値がなかった場合はどうなるのでしょうか?

inject

ruby
1
2
(1..10).inject{|sum, num| p sum }
(1..10).inject{|sum, num| p num }

動画であった通り、injectの引数がない場合は、配列の最初の要素がsumに入り、初期値となります。

そしてnumには、初めの要素は入らず、二つ目の要素から入ることになります。
初期値を設定しない場合は、初期値は配列の最初の要素、「二つ目以降の要素がnumに入る。」ということを覚えておいてください。

次にhashの場合にもinjectメソッドを使用する事ができます。
例えば良くあるのがある一定以上の条件を満たすhashに関しては、配列に格納する。みたいな事ができます。

ruby
1
2
3
4
month_salary = { programan: 30, tanaka: 25, suzuki: 27, satou: 20 } # 4人の月収
twenty_over_salary_men = month_salary.inject([]) { |salary_array, (key, value) | salary_array << key if value > 20; salary_array }

# ↑月収20万円を超える給料をもらっているサラリーマンを配列(twenty_over_salary_men)に格納する。

①injectの引数に[]を指定する事で、配列を初期値に入れる。
salary_arrayに空配列が代入される。
②(key, value)にmonth_salaryのkey, valueが入る。
③一番初めはkeyにprograman、valueに30が入る。
④もしvalueが20よりも大きいならば、salary_arrayという配列にkeyを代入する。今回は30で20よりも大きいので、salary_arrayにprogramanが代入される。
⑤で一度実行を終了する。これにより新たにソースをかける
⑥ salary_arrayを最後に実行する事で要素が追加されたsalary_arrayという配列を返り値として返しtwenty_over_salary_menに代入される。

inject

これで配列、hashにもinjectメソッドが使える事が分かったと思います。
使い方は難しいですが、慣れれば今まで長く冗長なコードを書いていたのが簡潔に綺麗なコードをかける様になります。
なのでeachメソッドばかりを使うのではなく、injectメソッドでかけそうな時はinjectメソッドでかけそうな時はinjectメソッドで書く様努めましょう。
最後に最も処理を簡潔にかけるinjectメソッドの便利な使い方を紹介します。
それはシンボル型で引数を渡す事です。

ruby
1
2
3
(1..10).inject(:+) # 配列内の値を全て足す
(1..10).inject(:-)  # 配列内の値を全て引く
(1..10).inject(:*)  # 配列内の値を全てかける

inject

こういった省略した形で書く事ができます。
またinjectは初期値を決められる事が特徴にありました。
もちろんこの省略系にも初期値を決めて計算ができます。

ruby
1
2
3
(1..10).inject(45, :+) # 初期値45に配列内の値を全て足す
(1..10).inject(100, :-) # 初期値100に配列内の値を全て引く
(1..10).inject(10, :\*) # 初期値10に配列内の値を全てかける

こんな風にinjectの第一引数に初期値が設定され、第二引数に何をするかシンボルを与える事で簡潔に書く事ができます。

inject

まとめ

injectメソッドとは配列等の要素を一つずつ繰り返してブロック内で要素を注入し処理をするメソッドです。hashに対してもinjectメソッドが使えます。(1..10).inject(45, :+)みたいにシンボルを引数に渡す事で簡潔な書き方ができます。