- 公開日
Ruby の inject(reduce) メソッドを使いこなす
目次
Rubyのイテレータメソッドinject
(reduce
)の使い方を紹介してみようと思います。
文中では便宜上、inject
で表記しますがreduce
も等価なメソッドなので文中のinject
はreduce
に置き換え可能です
injectの基本的な使い方
Rubyのinject
はこんなふうに使えます。
enum.inject { |memo, item| block }
enum.inject(init) { |memo, item| block }
Sum(合計)を出す
これだけではわかりにくいと思うので合計を出す処理を書いてみましょう。
injectを使わないパターン
まずはinject
を使わないパターン。
sum = 0
(1..10).each { |i| sum += i }
puts sum # => 55
injectを使うパターン
これをinject
で書き直すとこう。
(1..10).inject(0) { |sum, i| sum + i }
# => 55
この場合、sumの初期値をinjectの引数の0
で初期化しています。ただinjectのデフォルト引数設定は0
なので下記のように省略することができます。
(1..10).inject { |sum, i| sum + i }
# => 55
さらにinjectにはシンボルで演算子を渡してうまいようにやってくれます。
(1..10).inject(:+)
# => 55
これが一番シンプルでエレガント。
Ruby 2.4+ の場合
Ruby 2.4 以上をお使いの場合、Array#sumを使って書くことができます。
(1..10).sum
# => 55
応用編1: 配列内の要素数をハッシュに
Hash.new(0)
で初期化してinject
.
[:great, :good, :bad, :good, :good, :bad, :awesome, :great].inject(Hash.new(0)) { |hash, key| hash[key] += 1; hash}
# => {:great=>2, :good=>3, :bad=>2, :awesome=>1}
each_with_object を使う
上記の例だとhash[key] += 1; hash
とやや冗長な書き方となっていますが、each_with_object
使うともう少しシンプルに書けます。
[:great, :good, :bad, :good, :good, :bad, :awesome, :great].each_with_object(Hash.new(0)) { |key, hash| hash[key] += 1}
# => {:great=>2, :good=>3, :bad=>2, :awesome=>1}
応用編2: フィボナッチ数列
inject
を利用したフィボナッチ数列ロジック。
(0..10).inject([1, 1]) { |fib, i| fib << fib[i] + fib[i+1] }
# => [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233]
応用編3: ループを止める
途中でループを止めて処理を終えたい場合、break
を使えば可能。
(1..5).inject([]) do |ary, number|
break ary if number == 4
ary << number
end
# => [1, 2, 3]
番外編: each_with_index
each_with_index
でもinject
は使える。
(1..10).each_with_index.inject(0) { |sum, (value, index)| sum + value }
# => 55
(1..10).each_with_index.inject(0) { |sum, (value, index)| sum + value + index}
# => 100