injectとeach_with_objectって何が違うのさ?

  • 73
    いいね
  • 0
    コメント
この記事は最終更新日から1年以上が経過しています。

injecteach_with_objectの違いが分からない。
どちらもEnumerableなオブジェクトの要素を使って何かしらのオブジェクトを得る。

でもやっぱり違いがあったのでメモ。

イメージで説明

  • injectは、各要素が力を合わせて一つのオブジェクトを作る
  • each_with_objectは、ターゲットとなるオブジェクトに対して、各要素を作用させる

挙動で説明

配列の各要素を2乗する処理を書く。表面上は同じ挙動をする。
でもdoの後のresultは、同じresultでも意味合いが違っていて、

  • injectの場合はresultにはブロック内で最後に評価した値が入る
  • each_with_objectの場合は、resultは常にeach_with_objectの引数として渡されたオブジェクトを指す
hoge.rb
# inject
[1, 2, 3].inject [] do |result, i|
  result << i*i
end

# each_with_object
[1, 2, 3].each_with_object [] do |i, result|
  result << i*i
end

injectを使いたい場合の例

たとえば配列の和を求める場合

hoge.rb
sum = [1, 2, 3].inject 0 do |s, i|
  s + i
end

(この場合はsum = [ 1, 2, 3].inject :+でいいけど敢えてこの形で。)

ここでeach_with_objectを使おうとしてしまうと、

hoge.rb
sum = [ 1, 2, 3].each_with_object 0 do |i, s|
  s += i
end

とすればよさげだけど、sは破壊的に変更できない0(Fixnum)を指しているので、
sumに入るのは0となってしまう。

each_with_objectを使いたい場合の例

たとえば[["Alice", 50], ["Bob", 40], ["Charlie", 70]]から、{"Alice" => 50, "Bob" => 40, "Charlie" => 70}を得たい場合、

hoge.rb
ret = [["Alice", 50], ["Bob", 40], ["Charlie", 70]].each_with_object({}) do |(key, value), hash|
  hash[key] = value
end

こんな感じでかける。

ここでinjectを使おうとすると、できないわけではないけど、
作成中のハッシュをブロックの最後に評価しないといけないので1行余分に必要になる。

hoge.rb
ret = [["Alice", 50], ["Bob", 40], ["Charlie", 70]].inject({}) do |hash, (key, value)|
  hash[key] = value
  hash    # ブロックの最後の評価を作成中のハッシュにしないといけない
end

参考:
プログラミングは素晴らしい - [プログラミング] each、each_with_object、inject、map

  • この記事は以下の記事からリンクされています
  • Ruby メモからリンク