##この記事について
最近始めたCodewarを通じて学べたことを少しずつアウトプット
##問題
Bob is preparing to pass IQ test. The most frequent task in this test is to find out which one of the given numbers differs from the others. Bob observed that one number usually differs from the others in evenness. Help Bob — to check his answers, he needs a program that among the given numbers finds one that is different in evenness, and return a position of this number.
! Keep in mind that your task is to help Bob solve a real IQ test, which means indexes of the elements start from 1 (not 0)
仲間外れの数値を見つけてその値を返してやるメソッドを作成してやる。
##例
以下であればiq_test("2 4 7 8 10")
だと7だけが奇数やから、インデックス番号に+1した数である3を返すって感じ。
iq_test("1 2 1 1")だと2が少数派やから2を返すようにさせる。
iq_test("2 4 7 8 10") => 3 // Third number is odd, while the rest of the numbers are even
iq_test("1 2 1 1") => 2 // Second number is even, while the rest of the numbers are odd
###僕の回答
def iq_test(numbers)
numbers = numbers.split().map{|num| num.to_i}
even_count = numbers.select {|num| num.even?}.count
odd_count = numbers.select {|num| num.odd?}.count
numbers.each_with_index do |num, i|
if even_count > odd_count
return i + 1 if num.odd?
else
return i + 1 if num.even?
end
end
end
①引数で渡されたデータをsplit
で配列にして、map
で配列の要素をto_i
で数字に変換
②select
を使用して、偶数となるデータだけ取り出してcount
メソッドで何個あるか数える
③奇数の数も②と同じように数える
④数字が入った配列をeach_with_index
で要素だけではなくて、インデックス番号も一緒に渡す。
⑤条件分岐で、偶数と奇数のどちらが少数派かを比べて少数派の方をreturn
で返すようにする
##ベストプラクティス
相変わらず短いコードやな。。。
def iq_test(numbers)
nums = numbers.split.map(&:to_i).map(&:even?)
nums.count(true) > 1 ? nums.index(false) + 1 : nums.index(true) + 1
end
###解説①
nums = numbers.split.map(&:to_i).map(&:even?)
numbersをsplit
で配列にして、そのままmap
で数値の変換して、さらに続けて2つ目のmap
で偶数かどうかを評価してtrue/falseの結果をnums
に入れている。
####map(&:to_i)って何者や?
each
やmap
ではブロックを使用して以下のようなパターンがよく使われる。
["1", "2", "3"].map{|v| v.to_i}
=> [1, 2, 3]
ここでプログラマーさんたちが「もっと短く書けるようにしたいなー」という思いを実現して、&演算子
を組み合わせて短く書くことができるしたんや。
#ノーマル
["1", "2", "3"].map{|v| v.to_i}
=> [1, 2, 3]
nums = numbers.split.map(&:to_i).map{|num| num.even?}
#&を使用
["1", "2", "3"].map(&:to_i)
=> [1, 2, 3]
nums = numbers.split.map(&:to_i).map(&:even?)
https://www.xmisao.com/2014/02/09/ruby-array-map-idiom-symbol-to-proc.html
mapメソッドはブロック付きののメソッドで、ブロック付きメソッドの引数にはもちろんブロックを渡す必要がある。
しかし、to_procメソッドを持つオブジェクトの前に&を置くと、ブロックとして解釈される
####procとは?
ブロックをオブジェクト化したものがproc
。
ブロックはeach
とかmap
のようなメソッド呼び出しのときにしか利用することができなくて、オブジェクトとして扱えない。そこで&
を使ってブロックをオブジェクト化したものがproc
となる。
###解説②
nums.count(true) > 1 ? nums.index(false) + 1 : nums.index(true) + 1
nums = numbers.split.map(&:to_i).map(&:even?)
でtそれぞれの数字を評価してtrue/falseを配列をnums
に入れて、trueの数が1以上であれば、偶数の方が多く、奇数の方が少ないということになる。
偶数が多ければ、奇数のインデックス番号+1を返して、偶数が1よりも多くなかったら偶数のインデックス番号+1を返すようになっている。
####最後に
コードの認識合ってるのか自信がないので、もし違うとこがあればぜひご指摘頂きたいです。