nilを含むデータでもソートがしたい!
よくあることですね。
こんなときはみなさんどうしてるんですかね?
例えば、果物を値段の高い順にソートしたいとき
fruits = [
{
name: 'apple', price: 200
},
{
name: 'orange', price: 400
},
{
name: 'plum', price: 300
}
]
sorted_fruits_from_price_higher = fruits.sort do |a, b|
b[:price] <=> a[:price]
end
p sorted_fruits_from_price_higher
こうしますね。
出力は
[{:name=>"orange", :price=>400}, {:name=>"plum", :price=>300}, {:name=>"apple", :price=>200}]
ちゃんとpriceが降順に表示されるはずです。
蛇足
sortモジュールについて
sortはrubyの標準のモジュールです。
ブロック要素として配列の各要素を2つ(a, b)送り出して、
戻り値が1のときはbをaの前に持ってきて、
戻り値が-1のときはaをbの前に持ってきます。
戻り値が0のときは何もしません。
「戻り値が1のときに順番を変更すると覚えておけば大丈夫です」
<=> について
<=> はruby の比較演算子です。
a <=> b としたときに、左側の値のほうが大きかったら 1 を返し、
右側の値のほうが大きかったら -1 を、
左右の値が同じだったら 0 を、
そして、左右の型が違う場合は nil を返します。※重要!!!
nilを含むデータをソートしたいとき
ここからが本題です!
先程のデータの中に値段がわからない果物が入ってきたらどうしましょう!
fruits = [
{
name: 'apple', price: 200
},
{
name: 'orange', price: 400
},
{
name: 'anonymous', price: nil
},
{
name: 'plum', price: 300
},
]
ソートをしてみます。。。
sorted_fruits_from_price_higher = fruits.sort do |a, b|
b[:price] <=> a[:price]
end
エラーが。。。
fruits.rb:16:in `sort': comparison of Hash with Hash failed (ArgumentError)
比較演算子 <=> の戻り値を確かめてみましょう
sorted_fruits_from_price_higher = fruits.sort do |a, b|
p b[:price] <=> a[:price]
b[:price] <=> a[:price]
end
1
nil
nil!?
先程の蛇足説明を思い出してください(読んでなくても大丈夫です)
- sortモジュールは -1 と 1 と 0 のどれかの戻り値に基づいて処理する
- <=> は型の違う値を比較するとnilを返却する
上の2つの規則から判断すると、nil と 数値(300)を比較した結果、nilが戻り値になり、その結果、エラーが起こっていた!?
sortの戻り値をnilにしたらだめなんですねー
結論: nilを含んだデータを比較するには?
ここまでくれば簡単ですね
if文を使いましょう
sorted_fruits_from_price_higher = fruits.sort do |a, b|
if !a[:price]
1
elsif !b[:price]
-1
else
b[:price] <=> a[:price]
end
end
↑こうすればnilの入ったデータを後ろに追いやってくれます
[{:name=>"orange", :price=>400}, {:name=>"plum", :price=>300}, {:name=>"apple", :price=>200}, {:name=>"anonymous", :price=>nil}]
fruits.select! {|a| a[:price]}
↑のように比較前に取り除いても大丈夫です。
[{:name=>"orange", :price=>400}, {:name=>"plum", :price=>300}, {:name=>"apple", :price=>200}]
以上です
いかがでしたでしょうか?