0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Ruby】inject(reduce)メソッドとeach_with_objectメソッド

Last updated at Posted at 2025-02-26

はじめに

Enumerable#inject(reduce)(たたみこみ演算)の使い方と Enumerable#each_with_object との違いをまとめます。

基本の使い方

  • 最後にブロックを実行した結果を返す
[1,2,3,4,5].inject{|result, item| result + item}
=> 15
[1,2,3,4,5].inject(10){|result, item| result + item}
=> 25  # 初期値を10に設定
  • 下記のようにeachを使っても同じ結果になる
result = 0
[1,2,3,4,5].each{|v| result += v}
p result
=> 15

注意点

①nilの要素があるとエラーが発生する

[1,2,nil,4,5].inject{|result, item| result + item}
=> TypeError (nil can't be coerced into Integer)
  • 対処法の一例
[1,2,nil,4,5].inject{|result, item| result + item.to_i }
=> 12
[1,2,nil,4,5].compact.inject{|result, item| result + item}
=> 12

②初期値は上書きしない

init = [1,2]
ret =
[10,20.30.40].inject(init) do |result, item|
  result << item *2
end
p ret
[1, 2, 20, 40, 60, 80]
p init
=> [1, 2, 20, 40, 60, 80]  # 初期値も上書きされている
  • 対処法
    • 破壊的な変更を行わない
init = [1,2]
ret =
[10,20,30,40].inject(init) do |result, item|
  result + [item * 2]
end
p ret
=> [1, 2, 20, 40, 60, 80]
p init
=> [1, 2] # 初期値は変更されない

③ruby-style-guldeではinjectよりもreduceを推奨

Prefer map over collect, find over detect, select over find_all, reduce over inject, include? over member? and size over length.

each_with_object との比較

  • each_with_object の引数は書き換えられることを想定
    • each_with_index の引数は実行結果として返るオブジェクト
    • ブロックが実行される毎に書き換えられることを想定している
value = [1,2]
ret = [10,20,30,40].each_with_object(value){|item, result| result << item * 2 }
p ret
=> [1, 2, 20, 40, 60, 80]
p value
=> [1, 2, 20, 40, 60, 80] # 実行結果と同じ

使い分け

  • each_with_object
    • 引数の {} はブロックが実行される毎に書き換えられる
    • ループ処理の場合、ブロック内で次のループにオブジェクトを渡す処理を書く必要はない
      【追記】※下記はinject メソッドと比較するための例の引用です。実際はto_h メソッドを使った方が良いというコメントをいただきましたので、追記します。
lower = 'a'..'z'
lower_to_upper = lower.each_with_object({}) do |char, hash|
  hash[char] = char.upcase # ブロック内は対応づけのみで良い
end
p lower_to_upper
=> {"a"=>"A", "b"=>"B", "c"=>"C", "d"=>"D", "e"=>"E", "f"=>"F", "g"=>"G", "h"=>"H", "i"=>"I", "j"=>"J", "k"=>"K", "l"=>"L", "m"=>"M", "n"=>"N", "o"=>"O", "p"=>"P", "q"=>"Q", "r"=>"R", "s"=>"S", "t"=>"T", "u"=>"U", "v"=>"V", "w"=>"W", "x"=>"X", "y"=>"Y", "z"=>"Z"}
  • inject
    • 引数の {} は書き換えられない
    • ループ処理の場合、ブロック内で次のループにオブジェクトを渡す処理を書く必要がある
lower = 'a'..'z'
lower_to_upper = lower.inject({}) do |hash, char|
  hash[char] = char.upcase
  hash # オブジェクトを渡す処理が必要になる
end
p lower_to_upper
=> {"a"=>"A", "b"=>"B", "c"=>"C", "d"=>"D", "e"=>"E", "f"=>"F", "g"=>"G", "h"=>"H", "i"=>"I", "j"=>"J", "k"=>"K", "l"=>"L", "m"=>"M", "n"=>"N", "o"=>"O", "p"=>"P", "q"=>"Q", "r"=>"R", "s"=>"S", "t"=>"T", "u"=>"U", "v"=>"V", "w"=>"W", "x"=>"X", "y"=>"Y", "z"=>"Z"}

おわりに

injecteach_with_object ではブロック引数の順番の違いに注意して使用したいと思います。

参考

0
0
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?