9
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Rubyでnilを含む配列をソートしたいとき

Last updated at Posted at 2019-03-21

nilを含むデータでもソートがしたい!

よくあることですね。
こんなときはみなさんどうしてるんですかね?
例えば、果物を値段の高い順にソートしたいとき

fruits.rb
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.rb
fruits =  [
  {
    name: 'apple', price: 200
  },
  {
    name: 'orange', price: 400
  },
  {
    name: 'anonymous', price: nil
  },
  {
    name: 'plum', price: 300
  },
]

ソートをしてみます。。。

fruits.rb
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)

比較演算子 <=> の戻り値を確かめてみましょう

fruits.rb
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文を使いましょう

fruits.rb
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.rb
fruits.select! {|a| a[:price]}

↑のように比較前に取り除いても大丈夫です。
[{:name=>"orange", :price=>400}, {:name=>"plum", :price=>300}, {:name=>"apple", :price=>200}]

以上です

いかがでしたでしょうか?

9
4
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?