Arrayを返すEnumerableモジュールのメソッドはeach_with_objectで書ける(while系を除く)

ということに気付いたのでメモ*1
これを頭に入れておくと「ああ、これ select に似てるのに、返値にレシーバから以外の要素を入れたいから select じゃ書けない…>_<」って時に「そうか each_with_object で書けば良いんだ」とササっと書ける(のではないかなと思った)。
ちなみに、個人的に「inject の代わりに each_with_object を使ってみよう」運動を実施中です。

each_with_objectの復習

each_with_object(obj) {|(*args), memo_obj| ... } -> object

与えられた任意のオブジェクトと要素をブロックに渡し繰り返し、最初に与えられたオブジェクトを返します。
instance method Enumerable#each_with_object (Ruby 2.3.0)

参考:

each_with_object はブロック引数のオブジェクトを破壊的に変更し続ける場合に便利なメソッド
each、each_with_object、inject、map - プログラミングは素晴らしい

逆に言えば、ブロックパラメータの memo_obj に破壊出来ないオブジェクト(Numericとか)を使うことは出来ません。注意しましょう。

次に整数の和を求める例を示す。

# each_with_object
# 破壊的に変更可能なオブジェクトでないと適用不可
sum = [ 1, 2, 3].each_with_object(0) do |i, j|
  j += i
end
# p sum #=> 0

each、each_with_object、inject、map - プログラミングは素晴らしい

各メソッド

to_a/entries
Enumerable#to_a
Enumerable#each_with_object([]) { |elem, ary| ary << elem }
map/collect
Enumerable#map { |elem| formula }
Enumerable#each_with_object([]) { |elem, ary| ary << formula }
find_all/select
Enumerable#select { |elem| formula }
Enumerable#each_with_object([]) { |elem, ary| ary << elem if formula }
reject
Enumerable#reject { |elem| formula }
Enumerable#each_with_object([]) { |elem, ary| ary << elem unless formula }
grepgrep_v は判断を if じゃなく unless に)
  • ブロックなし
Enumerable#grep(pattern)
Enumerable#each_with_object([]) { |elem, ary| ary << elem if pattern === elem }
  • ブロックあり
Enumerable#grep(pattern) { |elem| formula }
Enumerable#each_with_object([]) { |elem, ary| ary << formula if pattern === elem }
partition
Enumerable#partition { |elem| formula }
Enumerable#each_with_object([[], []]) do |elem, (ary_true, ary_false)| 
  if formula
    ary_true << elem
  else
    ary_false << elem
  end
end
first/take
Enumerable#take(n)
Enumerable#each_with_object([]).with_index { |(elem, ary), i| ary << elem if i < n }
take_while

…書けず。

drop

Enumerable#drop(n)
Enumerable#each_with_object([]).with_index { |(elem, ary), i| ary << elem unless i < n }
drop_while

…これも書けず。

残念

Enumerator#with_index の手助けが必要な場合もありました。
while 系はダメでしたね((flat_mapはムリっぽいのでやってないですorz))。

*1:速度に関しては無視する。