紙一重の積み重ね

アラフォーのエンジニアがなれる最高の自分を目指して、学んだことをこつこつ情報発信するブログです。

【AtCoder ABC027 A問題】selectとfindメソッドの動きの違いについて

f:id:yokoyantech:20190825134320p:plain

はじめに

AtCoderの過去問をA問題から穴埋め中です。

普通に解いてACだった問題を、もっと短くコード書けないかと思い、select使ったらWAだったのでメモします。

解いた問題

atcoder.jp

最初に書いたコード(AC)

  • これでACだったんですが、モッサリしてるコードです。
  • 無駄が多い。。
ls = gets.split.map(&:to_i)
if ls.uniq.size == 1
    puts ls[0]
else
    puts ls.select{|l| ls.count(l) == 1 }
end

リファクタ1回目(WA)

  • 要素の数を2で割った余りが1の要素を出したらいいじゃない、と思い書いたコードがこちら。見事にWAです。
ls = gets.split.map(&:to_i)
puts ls.select{|l| ls.count(l) % 2 == 1}
  • 正方形の場合、全部帰ってきてしまうのでダメ。
$ ruby a2.rb
5 5 5
5
5
5

selectではなく、findならACになります。

【メモ】selectとfindは動きが違う

考えるまでもなく、Rubyのメソッドが手に馴染んでないなと感じたので整理。

select:条件に合致する要素をすべて配列で返す

  • 2で割り切れる要素を配列で取得する。
    • 割り切れるすべての要素が返ってくる
$ irb
2.6.3-p62 :003 > (1..6).to_a.select{|n| n % 2 == 0}
 => [2, 4, 6]

ちなみに、find_allでも同じ。

2.6.3-p62 :006 > (1..6).to_a.find_all{|n| n % 2 == 0}
 => [2, 4, 6]

find:条件に合致する最初の要素を返す

  • 2で割り切れる要素を取得する。
    • 割り切れる最初の要素2が返ってくる
$ irb
2.6.3-p62 :004 > (1..6).to_a.find{|n| n % 2 == 0}
 => 2

detectでも同じ。

2.6.3-p62 :005 > (1..6).to_a.detect{|n| n % 2 == 0}
 => 2

リファクタ2回目(AC)

ということで、正しいリファクタリング結果はこちら。

ls = gets.split.map(&:to_i)
puts ls.find{|l| ls.count(l) % 2 == 1}

正方形の場合でもOK。

$ ruby a2.rb
5 5 5
5

おわりに

ACしたコードでも、もっといい解法がないか考えたり、他の人はどんなコードを書いているか、自分なりに試行錯誤できるのがAtCoderの良いところですね。まずはA問題をすべて埋められるように、がんばります。