はじめに
先日、こんなQiita記事(というかプログラミング問題)を書きました。
【Rubyプログラミング問題】値札分割メソッド(split_price)を作成してください - Qiita
解答をコメントしてくださったみなさん、どうもありがとうございました。
今回は僕の作った解答と、いただいた解答の中から僕が一番イケてると思ったコードを紹介したいと思います。
おさらい:出題したのはこんな問題でした
ユーザーが自由に入力できる値札データがある。
このデータ(文字列)を以下のようなルールで分割するメソッド、 split_price を作成せよ。
'110.0万円' => ['110.0', '万円']
'2015円' => ['2015', '円']
'1,123,456円' => ['1,123,456', '円']
'110 - 120万円' => ['110 - 120', '万円']
'2015円' => ['2015', '円']
'価格未定' => ['価格未定', '']
nil => ['', '']
僕の作った解答
僕が作った解答はこちらです。
def split_price(price_text)
regex = /([^万円]+)(万?円)/
regex.match(price_text).to_a[1..2] || [price_text.to_s, '']
end
簡単な解説
regex = /([^万円]+)(万?円)/ で正規表現オブジェクトを作ります。
この正規表現の意味は以下の二つのパートから成り立っています。
- 「万」でも「円」でもない文字が1つ以上続く文字列(
[^万円]+) - 「円」または「万円」(
万?円)
このパートはそれぞれ ( ) で囲まれているので、matchを実行するとキャプチャされます。(パターンに一致した場合)
具体的にはこんな結果になります。
regex = /([^万円]+)(万?円)/
# => /([^万円]+)(万?円)/
regex.match('110.0万円')
# => #<MatchData "110.0万円" 1:"110.0" 2:"万円">
regex.match('2015円')
# => #<MatchData "2015円" 1:"2015" 2:"円">
regex.match('価格未定')
# => nil
regex.match(nil)
# => nil
matchの実行結果はMatchDataです。
これをto_aで配列化すると、マッチした文字列全体とキャプチャされた文字列が配列で返ってきます。
また、nil.to_a は空の配列([])になります。
regex.match('110.0万円').to_a
# => ["110.0万円", "110.0", "万円"]
regex.match('2015円').to_a
# => ["2015円", "2015", "円"]
regex.match('価格未定').to_a
# => []
regex.match(nil).to_a
# => []
マッチした文字列に対しては返ってきた配列の後ろ2件を返せばOKです。
なので、[1..2]で配列の2番目から3番目までを取得しています。
一方、空の配列に対して[1..2]を呼ぶとnilが返ります。
nilは偽として扱われるため、||(OR条件)でつないで後ろに続くコードを実行します。
よってprice_textが"価格未定"やnilだったりした場合は [price_text.to_s, '']が呼ばれ、['価格未定', '']や['', '']が返ります。
解説は以上です。
言葉だとわかりにくい部分も多いと思うので、irbやPryを使って自分の手で動かしてみると良いかもしれません。
優秀賞の発表
さて、僕は上記のような模範解答を用意していたのですが、この問題を公開してみると僕よりもスマートな解答を作ってくれた方がたくさんいました(汗)。
僕もまだまだですね。もっと精進します。
中でも僕が一番「おおっ」と思ったのは @284km さんのこちらのコードです。
def split_price(price_text)
price_text.to_s.match(/([^万円]*)(.*)/)[1..2]
end
僕の解答例と基本的なロジックは似ていますが、正規表現を工夫してどんな文字列でもマッチするようになっているため、よりシンプルな実装になっています。
'110.0万円'.to_s.match(/([^万円]*)(.*)/)
# => #<MatchData "110.0万円" 1:"110.0" 2:"万円">
'2015円'.to_s.match(/([^万円]*)(.*)/)
# => #<MatchData "2015円" 1:"2015" 2:"円">
'価格未定'.to_s.match(/([^万円]*)(.*)/)
# => #<MatchData "価格未定" 1:"価格未定" 2:"">
nil.to_s.match(/([^万円]*)(.*)/)
# => #<MatchData "" 1:"" 2:"">
matchの結果がnilにならないので、||でつなぐような処理がいらなくなります。いやあ、素晴らしいです!
まとめ
というわけで、この記事では「値札分割メソッド」の解答例と優秀賞を紹介しました。
実装をシンプルにするためのポイントはやはり正規表現ですね。
正規表現をうまく活用できている人ほど、コードがシンプルになっていると思います。
「正規表現ってよく聞くけどさっぱりわかんないんです (><)」という方はオライリーの「詳説正規表現」をオススメします。
正規表現を全く知らない人でもよくわかるように手取り足取り教えてくれます。
みなさんも正規表現やRubyの便利な標準ライブラリを活用して、スマートなコードを書けるようになりましょう!
あわせて読みたい
他の方の解答は元記事のコメント欄にいろいろと載っています。
こちらも参考にしてみてください。
