1
0

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 1 year has passed since last update.

配列に格納されたハッシュを複数条件でソートしたい

Last updated at Posted at 2023-09-23

やりたいこと

まずは、sortメソッドを使い、以下の配列をheightの昇順(背の低い順)でソートします。

people = [{ name: 'Sato', weight: 20, height: 180 },
          { name: 'Suzuki', weight: 30, height: 170 },
          { name: 'Ito', weight: 60, height: 160 },
          { name: 'Watanabe', weight: 50, height: 150 }]

その後で、複数条件でのソート(マルチソート)を試します。
例として、heightが同じ数値の場合はweightの降順(体重が上の順)でソートするようにします。

sort_byを使った書き方も最後に紹介します。

sortメソッドを使用

heightの昇順でソート

配列の並び替えにはsortメソッドを使用します。

sortに2つの要素を含んだブロックを渡すと、宇宙船演算子<=>を用いて比較をしてくれます。

<=>は左側が大きいなら正の整数、同じなら0、左側が小さいなら負の整数を返す演算子です。

puts 1 <=> 2 # -1
puts 1 <=> 1 # 0
puts 2 <=> 1 # 1

<=>の返り値を利用してsortメソッドがソートしてくれるようです。
昇順に並べたい場合は、ブロックの第一引数(以下の例ではa)が小さくなるようにします。

puts(people.sort { |a, b| a[:height] <=> b[:height] })

# 結果
{:name=>"Watanabe", :weight=>50, :height=>150}
{:name=>"Ito", :weight=>60, :height=>160}
{:name=>"Suzuki", :weight=>30, :height=>170}
{:name=>"Sato", :weight=>20, :height=>180}

heightの昇順とweightの降順でソート

heightが同じ数値の場合はweightの降順でソートしたいと思います。

people2 = [{ name: 'Sato', weight: 20, height: 180 },
          { name: 'Suzuki', weight: 30, height: 170 },
          { name: 'Takahashi', weight: 40, height: 160 },
          { name: 'Tanaka', weight: 20, height: 160 },
          { name: 'Ito', weight: 60, height: 160 },
          { name: 'Watanabe', weight: 50, height: 150 }]

<=>同じ長さの配列を比較する場合、各要素の値が異なった時点でその大小に応じて1, 0, -1を返します。

puts [1, 2, 3] <=> [1, 3, 2] # -1 2つ目の要素が左の方が小さいから
puts [1, 2, 3] <=> [1, 2, 3] # 0
puts [1, 3, 2] <=> [1, 2, 3] # 1 2つ目の要素が左の方が大きいから

この返り値を利用してheightの昇順、weightの降順で並べるには、以下のように書きます。

puts(people2.sort { |a, b| [a[:height], -a[:weight]] <=> [b[:height], -b[:weight]] })

# 結果
{:name=>"Watanabe", :weight=>50, :height=>150}
{:name=>"Ito", :weight=>60, :height=>160}
{:name=>"Takahashi", :weight=>40, :height=>160}
{:name=>"Tanaka", :weight=>20, :height=>160}
{:name=>"Suzuki", :weight=>30, :height=>170}
{:name=>"Sato", :weight=>20, :height=>180}

weightは降順にしたいので、ブロックの第一引数(以下の例ではa)が大きくなるように、-a[:weight]のようにマイナスをつけています。これで、a[:weight]-1の時に+1になります。

マイナスをつける書き方がわかりづらければ、b[:weight]の方を左側の[]内に入れます。こちらの書き方でも同じ結果になります。

puts(people2.sort { |a, b| [a[:height], b[:weight]] <=> [b[:height], a[:weight]] })

# 結果
{:name=>"Watanabe", :weight=>50, :height=>150}
{:name=>"Ito", :weight=>60, :height=>160}
{:name=>"Takahashi", :weight=>40, :height=>160}
{:name=>"Tanaka", :weight=>20, :height=>160}
{:name=>"Suzuki", :weight=>30, :height=>170}
{:name=>"Sato", :weight=>20, :height=>180}

sort_byメソッドを使用

Enumerable#sort_byを使うともっと簡単に書くことができます。処理速度もsort_byの方が速いようです。

sort_byはブロックの評価結果を<=>で比較して、昇順でソートしてくれます。

heightの昇順でソート

puts(people.sort_by { |p| p[:height] })

# 結果
{:name=>"Watanabe", :weight=>50, :height=>150}
{:name=>"Ito", :weight=>60, :height=>160}
{:name=>"Suzuki", :weight=>30, :height=>170}
{:name=>"Sato", :weight=>20, :height=>180}

heightの昇順とweightの降順でソート

降順にしたいときはマイナスをつけます。

puts(people2.sort_by { |p| [p[:height], -p[:weight]] })

# 結果
{:name=>"Watanabe", :weight=>50, :height=>150}
{:name=>"Ito", :weight=>60, :height=>160}
{:name=>"Takahashi", :weight=>40, :height=>160}
{:name=>"Tanaka", :weight=>20, :height=>160}
{:name=>"Suzuki", :weight=>30, :height=>170}
{:name=>"Sato", :weight=>20, :height=>180}

おわりに

降順にするときになぜマイナスをつけるのか理解できずにいましたが、今回宇宙船演算子<=>について調べて色々試すことで理解できました。

sort_byの内部での処理については公式リファレンスで紹介されていますが、理解できてはいないです。。

参考

1
0
0

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
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?