すでにメンバーの場合は

無料会員登録

GitHubアカウントで登録 Pikawakaが許可なくTwitterやFacebookに投稿することはありません。

登録がまだの方はこちらから

Pikawakaにログイン

GitHubアカウントでログイン Pikawakaが許可なくTwitterやFacebookに投稿することはありません。

Ruby

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

ぴっかちゃん
ぴっかちゃん

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

mapメソッドの基本構文
1
2
3
4
5
6
7
配列.map { |変数| 実行する処理 }
# 実行する処理が複数行に渡る場合
配列.map do |変数|
実行する処理
end
irb | mapメソッドの使用例
1
2
3
4
5
6
irb(main):001:0> numbers = [10, 20, 30, 40]
=> [10, 20, 30, 40]
irb(main):002:0> new_numbers= numbers.map { |n| n * 2 }
=> [20, 40, 60, 80]
irb(main):003:0> new_numbers
=> [20, 40, 60, 80]

mapメソッドは、配列だけではなくHashに対しても使用する事が出来ます。返り値は、配列になります。

mapメソッドの基礎情報

この章では、mapメソッドの基礎情報について1つ1つ解説します。

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

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

irb | 配列の要素を2倍にした新しい配列を作成する
1
2
3
4
5
6
irb(main):001:0> numbers = [10, 20, 30, 40]
=> [10, 20, 30, 40]
irb(main):002:0> new_numbers= numbers.map { |n| n * 2 }
=> [20, 40, 60, 80]
irb(main):003:0> new_numbers
=> [20, 40, 60, 80]

処理の流れ

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

参考: プロを目指す人のためのRuby入門(p96ページ参考)

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

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

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

irb | mapメソッドをeachメソッドに置き換える
1
2
3
4
5
6
7
8
irb(main):004:0> numbers = [10, 20, 30, 40]
=> [10, 20, 30, 40]
irb(main):005:0> new_numbers = []
=> []
irb(main):006:0> numbers.each { |n| new_numbers << n*2 }
=> [10, 20, 30, 40]
irb(main):007:0>new_numbers
=> [20, 40, 60, 80]

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

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

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

irb | 配列の要素を2倍にした新しい配列を作成する
1
2
3
4
5
6
irb(main):001:0> numbers = [10, 20, 30, 40]
=> [10, 20, 30, 40]
irb(main):002:0> new_numbers= numbers.map { |n| n * 2 }
=> [20, 40, 60, 80]
irb(main):003:0> new_numbers
=> [20, 40, 60, 80]

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

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

collectメソッド

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

irb | collectメソッドを使う場合
1
2
3
4
irb(main):009:0> numbers = [10, 20, 30, 40]
=> [10, 20, 30, 40]
irb(main):010:0>new_numbers= numbers.collect { |n| n * 2 }
=> [20, 40, 60, 80]

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

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

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

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

mapメソッドの応用的な使い方

この章では、mapメソッドの応用的な使い方について解説します。

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

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

  1. ブロックの引数が1つであること
  2. ブロックで呼び出すメソッドに引数がないこと
  3. ブロック引数に対して、メソッドを呼び出すこと以外の処理がないこと
mapメソッドの省略した書き方
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に対してメソッドを呼び出しているだけ」なので、&:を使って省略してコードを書くことが出来ます。

Hashでmapメソッドを使う方法

mapメソッドは、配列だけではなくHashに対しても使用することができます。
キーがkeyに代入され、値がvalueに代入されます。

irb | Hashにmapメソッドを使う例
1
2
3
4
5
irb(main):001:0> h = { BANANA: 100, ORANGE: 200, MELON: 300 }
=> {:BANANA=>100, :ORANGE=>200, :MELON=>300}
irb(main):002:0> h.map { |key, value| [key.downcase, value] }
=> [[:banana, 100], [:orange, 200], [:melon, 300]]

ただし、返り値はハッシュではなく、配列になるので注意してください。返り値を配列ではなくHashにする場合は、to_hメソッドを使用します。

irb | 配列からHashに変換する
1
2
irb(main):003:0> h.map { |key, value| [key.downcase, value] }.to_h
=> {:banana=>100, :orange=>200, :melon=>300}

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

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

mapメソッドは元の値を変更しないのに対して、map!メソッドは元の値を書き換えます。

irb | mapメソッドを使用した場合
1
2
3
4
5
6
7
irb(main):017:0> array = ["a", "b", "c"]
=> ["a", "b", "c"]
irb(main):018:0> array.map {|s| s.upcase }
=> ["A", "B", "C"]
irb(main):019:0> array
=> ["a", "b", "c"] # 元の値はそのまま
irb | map!メソッドを使用した場合
1
2
3
4
5
6
irb(main):020:0> array = ["a", "b", "c"]
=> ["a", "b", "c"]
irb(main):021:0> array.map! {|s| s.upcase }
=> ["A", "B", "C"]
irb(main):022:0> array
=> ["A", "B", "C"] # 元の値が変更されてしまう!

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

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

データベースから特定の値だけ配列にする方法

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

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

ユーザーの名前だけ配列にする場合
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メソッドを使って解説していきます。

引数(age)に渡された値が20以上だったらageを返すメソッド
1
2
3
def filter_r20_arr(age)
age if age >= 20
end

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

mapメソッド内にfilter_r20_arrメソッドを使用する
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!メソッドは、元の値に対して変更してしまうので使用はなるべく避けるようにしよう

9

わかった!