🔖
Ruby の Enumerable,Array,Hash#select は false を取り除かない
久々にハマったので戒めとして覚書。
各種 #compact
系のメソッドなんですがこれらは『要素から nil
を取り除く』という挙動になります。
# 配列から要素が nil のオブジェクトを取り除く
pp [1, nil, 3, 4, nil].compact
# => [1, 3, 4]
# Hash の場合は値が nil の要素を取り除く
pp({ a: 1, b: nil, c: 3, d: nil, nil => 5 }.compact)
# => {:a=>1, :c=>3, nil=>5}
nil
も取り除くので false
も取り除きそうな雰囲気はあるんですが #compact
では false
は取り除きません。
# false はそのまま
pp [1, false, nil, 3].compact
# => [1, false, 3]
なので例えば次のようなコードを書いている場合には意図しない取り除きが行われます。
def create(name)
{
name: name,
# name が nil でなく空でない場合は name_size の要素を追加する
name_size: (name && !name.empty?) && name.size
}.compact
end
name = "homu"
pp create("homu") # => {:name=>"homu", :name_size=>4}
pp create(nil) # => {}
# 空の場合も name_size が入ってほしくないが入ってしまう
pp create("") # => {:name=>"", :name_size=>false}
こういう場合は #compact
ではなくて他の手段を用いて取り除く必要があります。
def create(name)
{
name: name,
name_size: (name && !name.empty?) && name.size
}.select { _2 } # 値が真の場合のみ絞り込む
end
name = "homu"
pp create("homu") # => {:name=>"homu", :name_size=>4}
pp create(nil) # => {}
# 意図する結果になる
pp create("") # => {:name=>""}
Discussion