すでにメンバーの場合は

無料会員登録

GitHubアカウントで登録 Pikawakaが許可なくTwitterやFacebookに投稿することはありません。

登録がまだの方はこちらから

Pikawakaにログイン

GitHubアカウントでログイン Pikawakaが許可なくTwitterやFacebookに投稿することはありません。

Ruby

【Ruby】 sortメソッドで配列とHashの中身を並び替える方法

ぴっかちゃん
ぴっかちゃん

sortメソッドとは、配列やハッシュの中身をある一定の規則で並び替えて、新しい配列を返すメソッドのことです。

sortメソッドを配列に使用した場合は、以下のコードのように配列の中身を昇順に並び替えた新しい配列を返します。

irb | 配列の中身を昇順に並び替える
1
2
irb(main):001:0> [3, 5, 1, 2, 4].sort
=> [1, 2, 3, 4, 5] #昇順に並び替えられた新たな配列を返す

そしてハッシュに使用した場合は、以下のコードのようにハッシュのkeyを基準にして昇順に並び替えた新しい配列を返します。

irb | ハッシュのkeyを昇順に並び替える
1
2
irb(main):002:0> { c: 3, a: 5, b: 7 }.sort
=> [[:a, 5], [:b, 7], [:c, 3]] #keyを昇順に並び替えた新たな配列を返す

この記事では、sortメソッドの「基本的なデータの並び替え方法」から「演算子<=>で比較方法を定義する応用的な使い方」まで学ぶことが出来ます。sortメソッドを使いこなしてデータを取得したい順番に並び替えられるようになりましょう!

基本的な使い方

まずは、sortメソッドの基本構文や基本的なデータの並び替え方法について解説します。

sortメソッドを使った事がない人や基礎を固めて理解したい人は、irbで実行できるようにサンプルコードを用いて解説しているので、手を動かしながら学んでいきましょう。

基本構文

sortメソッドは、以下のように配列ハッシュに使用することが出来るメソッドです。

基本構文-->
1
2
配列.sort
ハッシュ.sort

返り値は、配列やハッシュの中身を一定の規則で並び替えて新たに生成された配列です。デフォルトでは以下のコードのように昇順に並び替えられます。(詳細は後述します。)

irb | sortメソッドを配列に使用した場合
1
2
irb(main):001:0> [9, 5, 3, 1, 6].sort
=> [1, 3, 5, 6, 9] # 昇順に並び替えて生成された配列を返す
irb | sortメソッドをハッシュに使用した場合
1
2
3
# ハッシュの場合は、keyを基準にして昇順に並び替えられる
irb(main):002:0> { c: 3, a: 5, b: 7 }.sort
=> [[:a, 5], [:b, 7], [:c, 3]] # 並び替えて生成された配列を返す

sortメソッドは、以下のコードのように配列やハッシュ以外に使用する事が出来ないので注意してください。

irb | 文字列にsortメソッドを使用した場合
1
2
irb(main):003:0> "a, b, c".sort # 文字列には使用できない
NoMethodError (undefined method `sort' for "a, b, c":String)

データを昇順に並び替える

昇順とは、データの値が小さいものから順番に並べることです。例えば、数字なら1, 2, 3, 4...と数が小さい順番に並びます。

sortメソッドで各データを昇順に並び替える基本的な方法を解説します。

配列の場合【昇順】

一次元の配列の場合は、以下のコードのように配列.sortでデータを昇順に並び替える事が出来ます。

irb | 一次元配列にsortメソッドを使用した場合
1
2
irb(main):001:0> [3, 5, 1, 2, 4].sort
=> [1, 2, 3, 4, 5] #昇順に並び替えられた新たな配列を返す

二次元配列の場合は、配列内の「配列の最初の要素」を基準として昇順に並び替えられます。

例えば、[["Cherry", 1], ["Apple", 3], ["Orange", 2]]の二次元配列の場合は、以下の箇所が配列内の配列の最初の要素にあたります。

二次元配列の要素

この二次元配列にsortメソッドを使うと、上記の箇所を基準にデータが以下のコードのように昇順に並べ替えられます。

irb | 二次元配列にsortメソッドを使用した場合
1
2
irb(main):002:0> [["Cherry", 1], ["Apple", 3], ["Orange", 2]].sort
=> [["Apple", 3], ["Cherry", 1], ["Orange", 2]]

2個目の要素を基準に並び替えをする場合は、
応用の使い方の「二次元配列の2個目の要素を比較する方法」で解説します。

ハッシュの場合【昇順】

ハッシュの場合は、以下のコードのようにハッシュ.sortでkeyを基準に昇順に並び替える事が出来ます。

irb | ハッシュのkeyを基準に昇順に並び替える
1
2
irb(main):001:0> { c: 3, a: 5, b: 7 }.sort
=> [[:a, 5], [:b, 7], [:c, 3]] #keyを昇順に並び替えた新たな配列を返す

sortメソッドを使うと、ハッシュの{ key: value }が各自[key, value]の配列に変換されて、keyを基準に昇順に並び替えた二次元配列が返り値になります。

ハッシュのkeyを基準に昇順に並び替えた例

keyではなく値を比較したい場合は、ハッシュのvalueを比較して並び替える方法で解説します。

ポイント
  1. sortメソッドを使用すると、ハッシュのkeyを基準に昇順で並び替える
  2. 返り値は、ハッシュではなく[key, value]の要素を持つ二次元配列

データを降順に並び替える

降順とは、データの値が大きいものから順番に並べることです。例えば、数字なら5, 4, 3, 2, 1...と数が大きい順番に並びます。

sortメソッドを使って各データを降順に並び替える基本的な方法を解説します。

配列の場合【降順】

配列のデータを降順に並べ替えるには、sortメソッドでデータを昇順に並べ替えてから順番を反転させるreverseメソッドを実行します。

irb | 配列のデータを降順に並び替える
1
2
irb(main):001:0> [3, 5, 1, 2, 4].sort.reverse
=> [5, 4, 3, 2, 1]

二次元配列の場合は、以下のコードのように最初の要素を基準に降順に並び替えられます。

irb | 二次元配列のデータを降順に並び替える
1
2
3
4
5
6
irb(main):002:0> [["C", 1], ["A", 3], ["B", 2]].sort
=> [["A", 3], ["B", 2], ["C", 1]]

# 配列の最初の要素を基準に降順に並び替える
irb(main):003:0> [["C", 1], ["A", 3], ["B", 2]].sort.reverse
=> [["C", 1], ["B", 2], ["A", 3]]

ハッシュの場合【降順】

ハッシュの場合もsortメソッドでデータを昇順に並び替えてから、reverseメソッドで順番を反転させます。

irb | ハッシュのkeyを基準に降順に並び替える
1
2
3
4
5
irb(main):001:0> { c: 3, a: 5, b: 7 }.sort
=> [[:a, 5], [:b, 7], [:c, 3]] # 昇順に並び替える

irb(main):002:0> { c: 3, a: 5, b: 7 }.sort.reverse
=> [[:c, 3], [:b, 7], [:a, 5]] # 降順に並び替える

keyを基準に並び替えが行われます。

sort!メソッド

sort!メソッドは、レシーバ自身の値を変更する破壊的メソッドです。

例えばsortメソッドの場合は、以下のコードのように配列を格納したarrayを昇順に並び替えた新しい配列を返すだけで、元のarrayの中身は変わりません。

irb | sortメソッドを使用した場合
1
2
3
4
5
6
irb(main):001:0> array = [3, 5, 1, 2, 4]
=> [3, 5, 1, 2, 4]
irb(main):002:0> array.sort # sortメソッドを使用する
=> [1, 2, 3, 4, 5]
irb(main):003:0> array 
=> [3, 5, 1, 2, 4] # arrayは変更されない

しかし、sort!メソッドを使った場合は以下のコードのようにarrayをソートした新たな配列が返るのではなく、array自身が昇順に並び変わって返ります。

irb | sort!メソッドを使用した場合
1
2
3
4
5
6
irb(main):004:0> array = [3, 5, 1, 2, 4]
=> [3, 5, 1, 2, 4]
irb(main):005:0> array.sort! # sort!メソッドを使用する
=> [1, 2, 3, 4, 5]
irb(main):006:0> array
=> [1, 2, 3, 4, 5] # array自身が変更される

上記のコードのようにarray.sort!array(レシーバ)自身の中身が変更されてしまうと、もし他にarrayを参照している箇所があった場合に影響が出てしまいますね。

このような破壊的メソッドをどうしても使用する状況の場合は、レシーバ自身が変更されてしまう事を十分に配慮してください。

応用的な使い方

sortメソッドは、ブロックを用いて並び替え方法を定義する事が出来ます。

このブロックを用いた比較方法を使えば、reverseメソッドを使わずに降順に並び替えたり、ハッシュのkeyではなくvalueを基準に並び替えたりする事が出来ます。

それでは、ブロックを用いた場合の構文や各ソート方法を見ていきましょう!

ブロック構文

ブロックを用いて並び替え方法を定義する場合は、以下のような形式になります。

ブロック用いて比較方法を定義する場合-->
1
配列(or ハッシュ).sort { |a, b|  比較方法の定義 }

以下のコードのように、ブロックにa, bの2つの引数を渡し、比較方法の定義では<=>演算子で要素同士の比較を行います。

irb | 配列を昇順に並び替える例
1
2
irb(main):001:0> [2, 1, 3].sort { |a, b| a <=> b }
=> [1, 2, 3]

ブロックを用いた方法は、<=>演算子と流れが少し複雑なので1つ1つ解説します。まずは<=>演算子とは何か見ていきましょう!

<=>演算子とは?

<=>演算子とは、以下のコードのように2つのオブジェクトを比較して、1, -1, 0のいずれかを返します。

irb | <=> 演算子の使用例
1
2
3
4
5
6
7
8
irb(main):001:0> 1 <=> 5  # 右の方が大きいので負の数を返す
=> -1
irb(main):002:0> 5 <=> 1  # 左の方大きいので正の数を返す
=> 1
irb(main):003:0> 5 <=> 5  # どちらも同じなので 0 を返す
=> 0
irb(main):005:0> 1 <=> "a" # 比較出来ない時は nil を返す
=> nil

各返り値の条件は、以下の通りです。

  • 左辺が右辺より小さければ-1を返す
  • 左辺が右辺より大きければ1を返す
  • 左辺と右辺が同じであれば 0 を返す
  • 比較できない場合はnilを返す

sortメソッドのブロックでは、この<=>演算子でオブジェクト同士を比較させて返る値によって並び替えが行われます。

先ほどの以下のコードでは、[2, 1, 3]の要素をa, bに代入し、その都度左辺と右辺を<=>演算子で比較させて返る返り値(-1, 0, 1)によって、並び替えられた配列を作成しています。

irb | 配列を昇順に並び替える例
1
2
irb(main):001:0> [2, 1, 3].sort { |a, b| a <=> b }
=> [1, 2, 3]

また、以下のコードのように文字列と数値の要素が混ざる場合など<=>演算子でnilが返る場合は、エラーが発生するので注意しましょう。

irb | 要素同士が比較出来ない場合はエラーが発生する
1
2
3
4
5
irb(main):001:0> "a" <=> 1 # 比較出来ない場合はnilを返す
=> nil

irb(main):002:0> ["a", 1, 2].sort { |a,b| a <=> b }
ArgumentError (comparison of String with 1 failed)

次は、ブロックの{|a, b| a <=> b}をもう少し直感的に理解できるように、ブロック構文を使わなかった場合のsortメソッドと比較してみます。

ブロックを使わない場合と比較する

まずは、条件比較が a <=> bの場合を見ていきます。

以下のコードのようにブロック変数が|a, b|の順番で条件比較が a <=> bならば、 [2, 1, 3].sortと同じ様に並び替えられます。

irb | ブロック変数が|a, b|の順番で、条件比較が a <=> bの場合-->
1
2
3
4
5
6
7
8
9
10
11
# ブロックを使った場合
[2, 1, 3].sort { |a, b| a <=> b }
=> [1, 2, 3]

# ブロックを使わない場合
[2, 1, 3].sort
=> [1, 2, 3]

# 2つのコードは等しい
[2, 1, 3].sort { |a, b| a <=> b } == [2, 1, 3].sort
=> true

次は、条件比較がb <=> aの場合を見ていきます。
以下のコードのようにブロック変数が|a, b|の順番で、条件比較がb <=> aならば [2, 1, 3].sort.reverseと同じ様に並び替えられます。

irb | ブロック変数が|a, b|の順番で条件比較が b <=> aの場合 -->
1
2
3
4
5
6
7
8
9
10
11
# ブロックを使った場合
[2, 1, 3].sort { |a, b| b <=> a }
=> [3, 2, 1]

# ブロックを使わない場合
[2, 1, 3].sort.reverse
=> [3, 2, 1]

# 2つのコードは等しい
[2, 1, 3].sort { |a, b| b <=> a } == [2, 1, 3].sort.reverse
=> true

このようにブロック構文を使わない場合の並び替えと比較すると、直感的にブロック内が何をしているのか理解出来ますね。次は、ブロックを使った目的ごとの並び替え方法を詳しく解説します。

配列の並び替えを定義する

以下の項目の配列の並び替えを定義する方法を解説します。

  • 一次元配列を降順にする方法
  • 二次元配列を降順にする方法
  • 2個目の要素を比較して並べ替える方法

一次元配列を降順にする方法

一次元配列を降順にする場合は、以下のように記述します。

一次元配列を降順にする場合-->
1
一次元配列.sort { |a, b| b <=> a }
irb | 一次元配列を降順にしたサンプルコード
1
2
irb(main):001:0>  [3, 5, 1, 2, 4].sort { |a, b| b <=> a }
=> [5, 4, 3, 2, 1] # 降順にソートされた新しい配列を返す

また、以下のコードのように比較する要素に数字の文字列が混ざっている場合はエラーが発生するので、ブロック内にto_iメソッドを使用して比較出来るようにしましょう。

irb | 要素に文字列の数が混ざっている場合
1
2
3
4
5
irb(main):001:0> [3, "5", 1, "2", 4].sort { |a, b| b <=> a }
ArgumentError (comparison of Integer with String failed)

irb(main):002:0> [3, "5", 1, "2", 4].sort { |a, b| b.to_i <=> a.to_i }
=> ["5", 4, 3, "2", 1] # 要素を数値に変換しているわけでは無い

二次元配列を降順にする方法

二次元配列を降順にする場合は、一次元配列と同様に以下のように記述します。

二次元配列を降順にする場合-->
1
二次元配列.sort { |a, b| b <=> a }

ただ二次元配列の場合は、以下のコードの"C", "A", "B"のように配列の最初の要素を比較して降順に並び替えられます。

irb | 二次元配列を降順にする場合
1
2
irb(main):001:0> [["C", 1], ["A", 3], ["B", 2]].sort { |a, b| b <=> a }
=> [["C", 1], ["B", 2], ["A", 3]] # 最初の要素を基準にソートされる

このようにブロック引数にa, bを渡すと、最初の要素を比較して並び替えが行われます。

二次元配列の2個目の要素を比較する方法

二次元配列の2個目の要素を比較して降順に並び替える場合は、以下のように記述します。

2個目の要素を比較して降順に並び替える場合-->
1
二次元配列.sort { |a, b| b[1] <=> a[1] } # [1]は配列のindex

ブロック内の[1]配列の添字(インデックス)です。配列の添字は0から番号を振られるので、配列内の2個目の要素を比較するには、b[1] <=> a[1]を指定します。

このように指定することで、以下のコードでは3, 2, 1と2個目の要素を基準にして降順に並び替えられてますね。

irb | 2個目の要素を比較して降順にしたサンプルコード
1
2
irb(main):001:0> [["C", 1], ["A", 3], ["B", 2]].sort { |a, b| b[1] <=> a[1] }
=> [["A", 3], ["B", 2], ["C", 1]] #  2個目の要素を基準にして降順にソート

また2個目の要素を比較して昇順にする場合は、以下のコードのようにブロック内のb[1] <=> a[1]を逆にします。

2個目の要素を比較して昇順に並び替える場合-->
1
二次元配列.sort { |a, b| a[1] <=> b[1] } # [1]は配列のindex
irb | 2個目の要素を比較して昇順にしたサンプルコード
1
2
irb(main):002:0> [["C", 1], ["A", 3], ["B", 2]].sort { |a, b| a[1] <=> b[1] }
=> [["C", 1], ["B", 2], ["A", 3]] #  2個目の要素を基準にして昇順にソート
ポイント
  1. ブロック引数a, bに比較した添字を指定すると、その要素を基準に並び替える事が出来る
  2. ブロック内の比較をb[1] <=> a[1]にすると、2個目の要素を比較して降順に並び替える
  3. 逆にa[1] <=> b[1]にすると、2個目の要素を比較して昇順に並び替える

ハッシュのvalueを比較して並び替える

デフォルトでは、以下のコードのようにvalueではなくkeyを比較して並び替えが行われます。

irb | keyを比較して昇順に並び替える例
1
2
3
4
irb(main):001:0> hash = { c: 3, a: 2, b: 1 }
=> {:c=>3, :a=>2, :b=>1}
irb(main):002:0> hash.sort
=> [[:a, 2], [:b, 1], [:c, 3]] # keyを基準に昇順に並び替えられる

ハッシュのvalueを比較して並び替えを行いたい場合は、ブロックに渡す引数を以下のように記述する必要があります。(引数の名前は別の名前でも大丈夫です。)

ハッシュのvalueを比較して並び替えたい場合-->
1
ハッシュ.sort { |(key1, val1), (key2, val2)| 比較方法を定義 }

ハッシュのvalueを比較して昇順に並び替える場合は、以下のコードのように記述します。

irb | ハッシュのvalueを基準に昇順に並び替える -->
1
2
3
4
# hash = { c: 3, a: 2, b: 1 }

hash.sort { |(key1, val1), (key2, val2)| val1 <=> val2 }
# => [[:b, 1], [:a, 2], [:c, 3]] # valueが昇順に並び替えられる

ハッシュのvalueを比較して降順に並び替える場合は、以下のコードのように記述します。

irb | ハッシュのvalueを基準に降順に並び替える -->
1
2
3
4
# hash = { c: 3, a: 2, b: 1 }

hash.sort { |(key1, val1), (key2, val2)| val2 <=> val1 }
# => [[:c, 3], [:a, 2], [:b, 1]] # valueが降順に並び替えられる

sort_byメソッドを使う

比較方法が複雑な場合は、sort_byメソッドを使う事で速度の低下を防ぐ事が出来ます。また、sortメソッドでブロックを用いた方法よりもシンプルに記述する事が出来ます。

例えば、先ほどのハッシュのvalueを比較して昇順に並び替えたサンプルコードでは以下のように記述していました。

irb | ハッシュのvalueを基準に昇順に並び替える -->
1
2
3
4
# hash = { c: 3, a: 2, b: 1 }

hash.sort { |(key1, val1), (key2, val2)| val1 <=> val2 }
# => [[:b, 1], [:a, 2], [:c, 3]] # ハッシュのvalueが昇順に並び替えられる

しかし、sort_byメソッドを使えば以下のコードのように記述する事が出来ます。

irb | sort_byメソッドでハッシュのvalueを基準に昇順に並び替える-->
1
2
3
4
# hash = { c: 3, a: 2, b: 1 }

hash.sort_by { |key, val| val }
# => [[:b, 1], [:a, 2], [:c, 3]]

また、二次元配列の2個目の要素を比較して昇順に並び替えたい場合も以下のコードのように記述する事が出来ます。

irb | sortメソッドを使った場合-->
1
2
[["C", 1], ["A", 3], ["B", 2]].sort { |a, b| a[1] <=> b[1] }
# => [["C", 1], ["B", 2], ["A", 3]]
irb | sort_byメソッドを使った場合-->
1
2
[["C", 1], ["A", 3], ["B", 2]].sort_by { |a| a[1] }
# => [["C", 1], ["B", 2], ["A", 3]]

sort_byメソッドで降順に並び替えたい場合は、以下の-a[1]のように-を付けます。

irb | sort_byメソッドで降順に並べ替える場合-->
1
2
[["C", 1], ["A", 3], ["B", 2]].sort_by { |a| -a[1] }
=> [["A", 3], ["B", 2], ["C", 1]]

データの並び替えが複雑になる場合は、sort_byメソッドを使用するようにしましょう!

この記事のまとめ

  • sortメソッドとは、データをある一定の規則で並び替えて、新しい配列を返すメソッドのこと
  • 配列とハッシュに使用する事が出来るメソッド
  • データの並べ替えが単純な場合はsortメソッドを使って、複雑な場合はsort_byメソッドを使おう!