LoginSignup
1
0

Rubyのeachとmapを比較してみた

Posted at

はじめに

転職をきっかけにRubyを触り始めており、プロダクションコードや書籍、チュートリアルでeachmapが多用されいたのですが、メソッドの処理が似ているということもあり混乱してしまいました。

そこで、この二つのインスタンスメソッドの共通点や異なる点をリファレンスマニュアルを参考にしてまとめてみました。

eachとmapの共通点

共通点は以下の二つです。

  • Arrayクラスのインスタンスメソッドである
  • ブロックを省略した場合はEnumeratorが返る

Arrayのリファレンスマニュアルのインスタンスメソッドを確認すると、eachmapがあるのでどちらもArrayクラスのインスタンスメソッドであることがわかります。

また、irb(interactive Ruby)でブロックを省略した挙動を確認してみるとEnumeratorというものが返ってくることがわかります。

irb(main):001> [1, 2, 3].each
=> #<Enumerator: ...>
irb(main):002> [1, 2, 3].map
=> #<Enumerator: ...>

Enumeratorって何?

リファレンスには

each 以外のメソッドにも Enumerable の機能を提供するためのラッパークラスです。また、外部イテレータとしても使えます。

とあります。
Enumerableは繰り返しを行うためのモジュールで、このモジュールをインクルードすることでEnumerableに定義されたインスタンスメソッドが使えるようになります。

Enumerableのリファレンスでは以下のように記述されています。

繰り返しを行なうクラスのための Mix-in。このモジュールのメソッドは全て each を用いて定義されているので、インクルードするクラスには each が定義されていなければなりません。

実際にEnumerableをインクルードしてみて確認します。

irb(main):035* class MyClass
irb(main):036*   include Enumerable # <= Enumerableをインクルード
irb(main):037*   
irb(main):038*   def each; end
irb(main):039> end
=> :each
irb(main):040> MyClass.instance_methods # <= インスタンスメソッドを確認
=> 
[:each,
 :take_while,
 :drop,
 :drop_while,
 :cycle,
 :chunk,
 :slice_before,
 :slice_after,
 :slice_when,
 ...]
# ↑ Enumerableで定義されているインスタンスメソッドが列挙される

まとめると、each,mapでブロックを省略した場合には、Enumerableという繰り返しを行うモジュールのラッパークラスであるEnumeratorクラスが返されるということですね。

また、Enumerableリファレンスの続きは以下のように記述されており、mapもArrayクラスでオーバーライドされているメソッドの一つです。

Array, Hash, Range, Enumerator等のクラスで、 Enumerableモジュールはインクルードされています。ただし、効率化のため、そのクラスでEnumerableと同名・同等の機能を再定義(オーバーライド)しているケースも少なくなく、特にArrayクラスでは同名のメソッドを再定義していることが多いです。

eachとmapの相違点

相違点は以下の二つです。

  • 処理内容
  • 戻り値(ブロックが与えられた場合)

リファレンスを比較して確認してみます。

each

each -> {|item| ... } -> self
各要素に対してブロックを評価します。

eachはブロック内の処理を評価するのみで要素を加工した結果を返しません。self(レシーバ自身)を返します。

[1, 2, 3, 4, 5].each { |i| i**2 } # <= ブロック内で要素の冪乗を返している
=> [1, 2, 3, 4, 5]                # <= 結果は変わらない

# object_idを確認してみる
arr = [1, 2, 3, 4, 5]
arr.object_id
=> 788540
new_arr = arr.each { |i| i**2 }
new_arr.object_id
=> 788540  # <= arrと同じ

map

map -> {|item| ... } -> [object]
各要素に対してブロックを評価した結果を全て含む配列を返します。

mapはブロック内の戻り値で新しい配列を作成して返します。

[1, 2, 3, 4, 5].map { |i| i**2 } # <= ブロック内で要素の冪乗を返している
=> [1, 4, 9, 16, 25]

# object_idを確認してみる
arr = [1, 2, 3, 4, 5]
arr.object_id
=> 788540
new_arr = arr.map { |i| i**2 }
new_arr.object_id
=> 993360  # <= arrと異なる
1
0
0

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
1
0