Jewel-mmo開発日記

RubyでMMORPGを作る過程を記録する日記。 Yokohama.rb 発起人。
2010-12-06

[ruby]Rubyのmapメソッドの使い方

(この記事はRuby Advent Calendar jp: 2010 : ATNDの6日目です。前日はokkezさんでした。)

Rubyにはmapメソッドというたいへん便利なメソッドがあります。

配列aにはいくつかの文字列が入ってるとします。

a = ["a", "abc", "abcdef"]

この文字列を次のように右寄せで表示したいとき、どう書けばいいでしょう。

     a
   abc
abcdef

いろんなやり方がありそうすが、ここではmapメソッドを使った例を紹介すると同時に mapメソッドの使い方について詳しく解説します。

まずeachメソッドで書いてみる

まずその前に。

mapメソッドを使った書き方を紹介する前により易しいeachメソッドを使って書いてみます。

a = ["a", "abc", "abcdef"]
l = 6
a.each {|e| puts e.rjust(l) }

実行結果

     a
   abc
abcdef

eachメソッドで配列の値をひとつひとつ順番に取り出してputsしています。 配列から取り出した値は変数eに代入され、要素数だけブロックが繰り返し実行されます。 (eはただの変数名です。別にeでなくても任意の好きな名前をつけることができます。)

文字列をレシーバにしてrjustメソッドを呼び出すと、rjustメソッドは文字列を右詰にして返します(左の余白はスペースで埋められます)。右詰めしたあとの文字列の長さは引数で渡しています。

"a".rjust(6) #=> "     a"

rjustメソッドの挙動は上記のような感じです。

同じ処理をCやJavaで書くと、このeachの例のように繰り返しを使うことが多いのではないでしょうか。

mapメソッドを使ってみる

さてここからが本番です。

mapメソッドを用いた書き方を紹介します。

a = ["a", "abc", "abcdef"]
l = 6
a = a.map {|e| e.rjust(l) }
puts a

実行結果

     a
   abc
abcdef

配列の内容が変数eに順々に入ってくるのはeachのときと同じですが、 mapメソッドは戻り値で配列を返します。

上の例でmapメソッドが返す配列は、もとの配列aの要素それぞれにrjustメソッドを適用した結果になります。 具体的には配列aの中の文字列は、それぞれ次のように置き換えられて、その結果が新しい配列として作られます。

"a"      → "     a"
"abc"    → "   abc"
"abcdef" → "abcdef"

上の例ではmapメソッドの戻り値を改めてaに代入して全体を新しい配列に置き換えていますが、 ここでmap!メソッドを使えば、もとの配列aを直接書き換えてしまうこともできます。

a = ["a", "abc", "abcdef"]
l = 6
a.map! {|e| e.rjust(l) }
puts a

map!は、レシーバの内容を変更してしまう破壊的なメソッドです。

(おっと、説明を忘れていましたが、putsメソッドは引数に配列を渡すと要素それぞれを改行しながら表示してくれます。putsに渡すとき「a.join(\n)」として配列をひとつの文字列に変換してもいいのですが、幸いその必要がありません。)

実はaを置き換えなくても次のようにmapの戻り値を直接putsの引数にすることも可能です。

puts a.map {|e| e.rjust(l) }

(こうすると、aは置き換えられることなく最初の内容が保たれます。)

慣れないうちは結合の優先順位をイメージしにくいかもしれません。参考までに省略されているカッコを書くとこうなります。

puts(a.map {|e| e.rjust(l) })

もっとmapメソッドを使ってみる

さっきのコードでは、文字列の長さを6と決めて右詰の処理を行っていました。

l = 6

この6という数値は、配列の中に含まれる文字列の最も長い文字列の長さを表したものです。

ここでもmapメソッドを使えば最も長い文字列の長さを簡単に求めることができます。

a = ["a", "abc", "abcdef"]
b = a.map {|e| e.length }
l = b.max
puts l #=> 6が表示される

aの要素に対してlengthメソッドを適用した結果をbに代入しています。 bは次のような配列になります。

[1, 3, 6]

maxメソッドは配列の中から最も大きい値を探し出して返してくれます。

[1, 3, 6].max #=> 6

なお、上の例は変数bを省略して次のように書くこともできます。

l = a.map {|e| e.length }.max

まとめ

まとめます。

a = ["a", "abc", "abcdef"]

上記のようにaが文字列の配列だった場合、mapを使うと…

l = a.map {|e| e.length }.max
puts a.map {|e| e.rjust(l) }

このような2行で、当初の目的を達成することができます。 特に1行目の最も長い文字列の文字数を求めるところでは、eachよりmapを使ったほうがすっきり書けます。

mapメソッドはとっても便利でかつ使えるケースが多いので、ぜひともこの機会に覚えてください!