【Ruby】mapメソッドの基礎から応用をマスターして、効率的なコードを書けるようにしよう!

Ruby

mapメソッドとは?

mapメソッドは、配列の要素の数だけブロック内で処理を繰り返して、新しい配列を返します。

mapメソッドの構文

ruby
1
2
3
4
5
6
配列.map { |変数| 実行する処理 }


配列.map do |変数|
  実行する処理が複数ある
end
  • mapメソッドにわたすBlock内の処理が複数行に渡る処理の場合は {}ではなく do-endを用いて記述する場合が多い
  • 配列だけではなく、ハッシュに対しても利用することが出来る(返り値は配列になる)

mapメソッドの基本的な使い方

mapメソッドの使い方は簡単です。
例えば、配列の要素を2倍にした新しい配列を作る場合は、下記の通りになります。

ruby
1
2
3
4
numbers = [10, 20, 30, 40]
new_numbers= numbers.map { |n| n * 2 }

new_numbers #=> [ 20, 40, 60, 80]

処理の流れ

  1. まず、配列の最初の要素である10がブロック{}に渡されて変数nに代入されます
  2. n*2(10*2)の結果が新しい配列に代入されます
  3. この流れが配列の要素分ループ処理されます
  4. 新しい配列は、new_numbers変数に代入されます

eachメソッドをmapメソッドに置き換えて比較してみよう!

配列を扱うメソッドに使用頻度が高いeachメソッドがありますが、場合によってはmapメソッドで置き換えることが出来ます。

先ほどのmapメソッドのコードをeachメソッドに置き換えると、下記の通りになります。

ruby
1
2
3
4
5
numbers = [10, 20, 30, 40]

new_numbers = []
numbers.each { |n| new_numbers << n*2 }
new_numbers #=> [ 20, 40, 60, 80]

eachメソッドを使うと、空配列new_numbersを用意して、ブロック内の処理結果を配列に入れなければならないので、コード量が少し増えてしまいます。

さらに、ブロック内で、new_numbersに対して、<< n*2の処理も書いているので、冗長な書き方になっています。

これを踏まえた上でもう一度mapメソッドのコードを確認しましょう。
mapメソッドを使用するとブロック内では処理の本質のみが書かれているので非常に分かりやすいです。

ruby
1
2
3
4
numbers = [10, 20, 30, 40]
new_numbers= numbers.map { |n| n * 2 }

new_numbers #=> [ 20, 40, 60, 80]

空の配列を用意して、他の配列を順番にループ処理をした結果を空の配列に入れる処理は、mapメソッドに置き換えることが出来ます!

このmapメソッドと同じ働きをするメソッドにcollectメソッドというものもあります。

collectメソッド

collectメソッドは、mapメソッドと全く同じ動作をするメソッドです。

ruby
1
2
3
numbers = [10, 20, 30, 40]
new_numbers= numbers.collect { |n| n * 2 }
=> [20, 40, 60, 80]

同じ動作のメソッドですが、名前の違いは発想の違いから来ているようです。

  • mapメソッド は、データ構造を保ったまま、あるルールに従い元のデータ構造を別データ構造に変換する発想です。

  • collect メソッドは、データ構造内の全ての要素に対して、ある処理を繰り返し実行し、その結果を集めたものという発想です。

異なる発想によって実装されたメソッドですが、動作は同じなので、どちらを使っても大丈夫です。

mapメソッドの応用

次に、mapメソッドの応用の使い方をみていきましょう。

mapメソッドの省略した書き方とは?

mapメソッドは、下記の3つの条件に合えば、ブロックを渡す代わりに&:メソッド名を使って省略して書くことが出来ます。

  1. ブロックの引数が1つであること
  2. ブロックで呼び出すメソッドに引数がないこと
  3. ブロック引数に対して、メソッドを呼び出すこと以外の処理がないこと
ruby
1
2
3
4
5
6
7
# 通常の書き方
["RUBY", "PHP", "JAVA"].map { |s| s.downcase }
=> ['ruby', 'php', 'java']

# 省略した書き方
["RUBY", "PHP", "JAVA"].map(&:downcase)
=> ["ruby", "php", "java"]

上記のコードは、「ブロック引数のsが1つで、downcaseに引数がなく、sに対してメソッドを呼び出しているだけ」なので、&:を使って省略してコードを書くことが出来ます。

配列だけではなく、ハッシュでもmapメソッドを使ってみよう

mapメソッドは、配列だけではなくハッシュに対しても使用することができます。

キーがkeyに代入され、値がvalueに代入されます。

ruby
1
2
3
4
h = { BANANA: 100, ORANGE: 200, MELON: 300 }
h.map { |key, value| [key.downcase, value] }

=> [[:banana, 100], [:orange, 200], [:melon, 300]]

ただし返り値はハッシュではなく、配列になるので注意してください。

返り値をハッシュにしたい場合は、to_hメソッドを使うことによって配列からハッシュに変換する事が出来ます。

ruby
1
2
h.map { |key, value| [key.downcase, value] }.to_h
=> {:banana=>100, :orange=>200, :melon=>300}

mapメソッドとmap!メソッドの違いとは?

rubyでは、後ろに「!」がつくメソッドを「破壊的メソッド」と呼びます。
破壊的メソッドとは、元の値そのものを変更してしまうメソッドのことです。

mapメソッドは元の値を変更しないのに対して、map!メソッドは元の値を書き換えます。
実際にコードで比較して確認してみましょう。

mapメソッド

ruby
1
2
3
4
5
6
7
array = ["a", "b", "c"]
array.map {|s|  s.upcase }
=> ["A", "B", "C"]

# 元の値はそのまま
array
=> ["a", "b", "c"]

map!メソッド

ruby
1
2
3
4
5
6
7
array = ["a", "b", "c"]
array.map! {|s|  s.upcase }
=> ["A", "B", "C"]

# 元の値が変更されてしまう!
array
=> ["A", "B", "C"]

mapメソッドを使った後、arrayの値が["a", "b", "c"]と変更していないことに対して、map!メソッドを使うとarrayの値が["A", "B", "C"]に変更されてしまう事が分かります。

破滅的メソッドは思わぬ所に影響が出てしまう場合があるので通常は使用を避けるようにしましょう。

mapメソッドのもっと便利な使い方をマスターしよう!

mapメソッドを使って、全てのユーザーの名前だけを配列で取得してみよう。

mapメソッドは、Railsでテーブルから特定のカラムの値を取得したいときに便利です。

usersテーブルの全ユーザーの名前を取得するとき、mapメソッドを使えば簡単に取得する事が出来ます。

ruby
1
2
3
4
users = User.all
users.map { |user| user.name }

=> ["Jacob", "Jackson", "Noah", "Lucas", "Sophia", "Chloe", "Abigail", "Harry"]

mapメソッド内にメソッドを利用する方法

mapメソッドは、ブロック内でメソッドを利用することで更に細かい条件の配列を作り出す事が出来ます。

今回は20歳以上だったら値を返すfilter_r20_arrメソッドを使って解説していきます。

ruby
1
2
3
def filter_r20_arr(age)
  age if age  >= 20
end

年齢がバラバラに入ったagesの配列の要素をr20_limited?メソッドの引数(age)に入れることによって、返り値は20歳以上の要素の配列にしています。

ruby
1
2
3
4
5
6
ages = [9, 11, 23, 45, 66, 12, 77]
filtered_r20_arr =  ages.map { |age|  filter_r20_arr(age) }
=> [nil, nil, 23, 45, 66, nil, 77]

filtered_r20_arr.compact
=> [23, 45, 66, 77]

この様に、mapメソッドはブロック内にメソッドを利用して更に新しい配列を作成する事が出来ます!

まとめ

  • mapメソッドは、配列の要素の数だけブロック内で処理を繰り返して、新しい配列を作成します
  • 配列だけではなく、ハッシュに対してもmapメソッドを使用する事が出来ます
  • map!メソッドは、元の値に対して変更してしまうので使用はなるべく避けるようにしましょう。