昨日、正規表現で作成したコードについて修正。
理由は「-5」を「5」として受けいれてしまえるためです。
以下のメソッドを使って考えます。
String#split
split(sep = $;, limit = 0) -> [String]
split(sep = $;, limit = 0) {|s| ... } -> self
sep で指定されたセパレータによって文字列を limit 個まで分割、結果を文字列の配列で返します。
ブロックを指定すると、配列を返す代わりに分割した文字列でブロックを呼び出します。
ブロックなしの場合は配列が返ります。
to_i
to_i(base = 10) -> Integer
文字列を 10 進数表現された整数であると解釈し、整数に変換します。
Array#filter
filter -> Enumerator
select {|item| ... } -> [object]
filter {|item| ... } -> [object]
ブロックを評価した値が真であった要素を全て含む配列を返します。
真になる要素がひとつもなかった場合は空の配列を返却します。
arrayクラスのインスタンスメソッドですが、引数にブロックを与えない場合は配列ではなくEnumeratorを返します。
Numeric#positive?
positive? -> bool
self が 0 より大きい場合に true を返します。
そうでない場合に false を返します。
instance method Numeric#positive?
instance method Array#uniq
uniq -> Array
uniq {|item| ... } -> Array
配列から重複した要素を取り除いた新しい配列を返します。
取り除かれた要素の部分は前に詰められます。
たくさんメソッドをつなげたり入れ替えたりしたときには、処理によってレシーバのデータ型や注意が必要と実感しました。
uniqの場合
to_iの前にuniqを適用すると、「01」と「1」が異なるため重複として処理されません。
irb#1(main):133:0> "2,2,6,弐,⑧,0,,-5,01,1,Ⅳ".split(",").uniq.map(&:to_i).filter(&:positive?)
=> [2, 1, 1]
irb#1(main):134:0> "2,2,6,弐,⑧,0,,-5,01,1,Ⅳ".split(",").map(&:to_i).uniq.filter(&:positive?)
=> [2, 1]
irb#1(main):135:0> "2,2,6,弐,⑧,0,,-5,01,1,Ⅳ".split(",").map(&:to_i).filter(&:positive?).uniq
=> [2, 1]
filterの場合
map適用後は配列のデータは返されますが、ブロック内の処理で要素がハッシュ型に変換されているとエラーが生じてしまいます。
irb#1(main):152:0> "2,2,6,弐,⑧,0,,-5,01,1,Ⅳ".split(",").map(&:to_i).uniq.filter(&:positive?).map{|num| { "id" => num }}
=> [{"id"=>2}, {"id"=>1}]
irb#1(main):155:0> "2,2,6,弐,⑧,0,,-5,01,1,Ⅳ".split(",").map(&:to_i).map{|num| { "id" => num }}.uniq.filter(&:positive?)
(irb#1):155:in `filter': undefined method `positive?' for {"id"=>2}:Hash (NoMethodError)
また、繋げることで可読性が下がり複雑性が増してしまうとよくありません。
過剰な場合はrubocop でも警告が表示されます。
app/forms/bill_group_form.rb:119:5: C: Metrics/PerceivedComplexity: Perceived complexity for xxxxxxxxxxxxxxxxxxxxxxx is too high. [9/8]
def xxxxxxxxxxxxxxxxxxxxxxx
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^