更新日:
【Ruby】 sortメソッドで配列とHashの中身を並び替える方法
sortメソッドとは、配列やハッシュの中身をある一定の規則で並び替えて、新しい配列を返すメソッドのことです。
sortメソッドを配列に使用した場合は、以下のコードのように配列の中身を昇順に並び替えた新しい配列を返します。
1
2
irb(main):001:0> [3, 5, 1, 2, 4].sort
=> [1, 2, 3, 4, 5] #昇順に並び替えられた新たな配列を返す
そしてハッシュに使用した場合は、以下のコードのようにハッシュの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
返り値は、配列やハッシュの中身を一定の規則で並び替えて新たに生成された配列です。デフォルトでは以下のコードのように昇順に並び替えられます。(詳細は後述します。)
1
2
irb(main):001:0> [9, 5, 3, 1, 6].sort
=> [1, 3, 5, 6, 9] # 昇順に並び替えて生成された配列を返す
1
2
3
# ハッシュの場合は、keyを基準にして昇順に並び替えられる
irb(main):002:0> { c: 3, a: 5, b: 7 }.sort
=> [[:a, 5], [:b, 7], [:c, 3]] # 並び替えて生成された配列を返す
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
でデータを昇順に並び替える事が出来ます。
1
2
irb(main):001:0> [3, 5, 1, 2, 4].sort
=> [1, 2, 3, 4, 5] #昇順に並び替えられた新たな配列を返す
二次元配列の場合は、配列内の「配列の最初の要素」を基準として昇順に並び替えられます。
例えば、[["Cherry", 1], ["Apple", 3], ["Orange", 2]]
の二次元配列の場合は、以下の箇所が配列内の配列の最初の要素にあたります。
この二次元配列にsortメソッドを使うと、上記の箇所を基準にデータが以下のコードのように昇順に並べ替えられます。
1
2
irb(main):002:0> [["Cherry", 1], ["Apple", 3], ["Orange", 2]].sort
=> [["Apple", 3], ["Cherry", 1], ["Orange", 2]]
2個目の要素を基準に並び替えをする場合は、
応用の使い方の「二次元配列の2個目の要素を比較する方法」で解説します。
ハッシュの場合【昇順】
ハッシュの場合は、以下のコードのようにハッシュ.sort
で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ではなく値を比較したい場合は、ハッシュのvalueを比較して並び替える方法で解説します。
データを降順に並び替える
降順とは、データの値が大きいものから順番に並べることです。例えば、数字なら5, 4, 3, 2, 1...
と数が大きい順番に並びます。
sortメソッドを使って各データを降順に並び替える基本的な方法を解説します。
配列の場合【降順】
配列のデータを降順に並べ替えるには、sortメソッドでデータを昇順に並べ替えてから順番を反転させるreverseメソッドを実行します。
1
2
irb(main):001:0> [3, 5, 1, 2, 4].sort.reverse
=> [5, 4, 3, 2, 1]
二次元配列の場合は、以下のコードのように最初の要素を基準に降順に並び替えられます。
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メソッドで順番を反転させます。
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
の中身は変わりません。
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
自身が昇順に並び変わって返ります。
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つの引数を渡し、比較方法の定義では<=>
演算子で要素同士の比較を行います。
1
2
irb(main):001:0> [2, 1, 3].sort { |a, b| a <=> b }
=> [1, 2, 3]
ブロックを用いた方法は、<=>
演算子と流れが少し複雑なので1つ1つ解説します。まずは<=>
演算子とは何か見ていきましょう!
<=>演算子とは?
<=>演算子とは、以下のコードのように2つのオブジェクトを比較して、1, -1, 0
のいずれかを返します。
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)によって、並び替えられた配列を作成しています。
1
2
irb(main):001:0> [2, 1, 3].sort { |a, b| a <=> b }
=> [1, 2, 3]
また、以下のコードのように文字列と数値の要素が混ざる場合など<=>演算子でnil
が返る場合は、エラーが発生するので注意しましょう。
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
と同じ様に並び替えられます。
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
と同じ様に並び替えられます。
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 }
1
2
irb(main):001:0> [3, 5, 1, 2, 4].sort { |a, b| b <=> a }
=> [5, 4, 3, 2, 1] # 降順にソートされた新しい配列を返す
また、以下のコードのように比較する要素に数字の文字列が混ざっている場合はエラーが発生するので、ブロック内にto_iメソッドを使用して比較出来るようにしましょう。
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"
のように配列の最初の要素を比較して降順に並び替えられます。
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個目の要素を比較して降順に並び替える場合は、以下のように記述します。
1
二次元配列.sort { |a, b| b[1] <=> a[1] } # [1]は配列のindex
ブロック内の[1]
は配列の添字(インデックス)です。配列の添字は0から番号を振られるので、配列内の2個目の要素を比較するには、b[1] <=> a[1]
を指定します。
このように指定することで、以下のコードでは3, 2, 1
と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]
を逆にします。
1
二次元配列.sort { |a, b| a[1] <=> b[1] } # [1]は配列のindex
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個目の要素を基準にして昇順にソート
ハッシュのvalueを比較して並び替える
デフォルトでは、以下のコードのようにvalueではなく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を比較して並び替えを行いたい場合は、ブロックに渡す引数を以下のように記述する必要があります。(引数の名前は別の名前でも大丈夫です。)
1
ハッシュ.sort { |(key1, val1), (key2, val2)| 比較方法を定義 }
ハッシュの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を比較して降順に並び替える場合は、以下のコードのように記述します。
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を比較して昇順に並び替えたサンプルコードでは以下のように記述していました。
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メソッドを使えば以下のコードのように記述する事が出来ます。
1
2
3
4
# hash = { c: 3, a: 2, b: 1 }
hash.sort_by { |key, val| val }
# => [[:b, 1], [:a, 2], [:c, 3]]
また、二次元配列の2個目の要素を比較して昇順に並び替えたい場合も以下のコードのように記述する事が出来ます。
1
2
[["C", 1], ["A", 3], ["B", 2]].sort { |a, b| a[1] <=> b[1] }
# => [["C", 1], ["B", 2], ["A", 3]]
1
2
[["C", 1], ["A", 3], ["B", 2]].sort_by { |a| a[1] }
# => [["C", 1], ["B", 2], ["A", 3]]
sort_byメソッドで降順に並び替えたい場合は、以下の-a[1]
のように-
を付けます。
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メソッドを使おう!