こんにちは! フリーエンジニアの長瀬です。
みなさんはsortを利用していますか?
rubyにたくさんの配列・ハッシュに関するメソッドが用意されています。
sortメソッドはその中の一つで配列やハッシュの中身をコマンド一つで並び替えることができます。
この記事では、rubyのsortについて
・ 配列をsortする
・ ハッシュをキーで昇順sortする
・ ハッシュをキーで降順sortする
・ ハッシュを値で昇順sortする
・ ハッシュを値で降順sortする
・ ハッシュのsort結果をto_hでハッシュにする
という順番で、初心者にわかりやすいように解説していきます。
配列をsortする
まずは簡単な配列のsortについてみていきましょう。
配列の基本的なsortはsortメソッドで実現できます。
また、sort!メソッドでレシーバー(今回は配列a)自体を変更する破壊的メソッドにすることもできます。
a = [2,3,1,4,5,1] p a.sort
[実行結果]
[1, 1, 2, 3, 4, 5]
こちらは昇順になっているので、降順でsortしたい場合はreverseメソッドと組み合わせます。
a = [2,3,1,4,5,1] p a.sort.reverse
[実行結果]
[5, 4, 3, 2, 1, 1]
また、sortメソッドだけでなくてsort_byメソッドでも同様の処理が可能です。
sortメソッドとの違いはsort_byメソッドではブロックを用いて処理を実行するという点です。
a = [2,3,1,4,5,1] p a.sort_by {|a| a} p a.sort_by {|a| a}.reverse
[実行結果]
[1, 1, 2, 3, 4, 5] [5, 4, 3, 2, 1, 1]
ブロックで処理できるので、sort_byメソッドを使った方が拡張性は高いです。
例えば文字列だど、文字の数でsortが可能です。
s = ["aaaaa","b","tt"] p s.sort_by {|array| array.size}
[実行結果]
["b", "tt", "aaaaa"]
ハッシュをキーでsortする
ハッシュをキーで昇順sortする
ハッシュをキーの昇順によってソートするには、単にハッシュのsortメソッドを使うだけで済みます。
Rubyでは一番よく使うソートなので、簡単に使えるようになっています。
h = { "def" => 2, "ghi" => 1, "abc" => 3 } p h.sort p h
[実行結果]
[["abc", 3], ["def", 2], ["ghi", 1]] {"def"=>2, "ghi"=>1, "abc"=>3}
以上のように、ハッシュのsortメソッドで、ハッシュのキーの昇順でソートできることが分かりました。
値の昇順ソートではないことも、確認できました。
そしてハッシュでsortを使う場合、戻り値は2次元の配列になっていることに注意しなければなりません。
もしも、ハッシュのままsortしたいのであれば、後述するto_hメソッドを使えば実現できます。
また、「p h」の出力結果からsortメソッドを使用したレシーバのハッシュそのものは、ソートされないままであることも確認できました。
ハッシュをキーで降順sortする
ハッシュをキーの降順によってソートするには、単にハッシュのsortメソッドの後に、reverseメソッドを使うだけで済みます。
Rubyではよく使う降順ソートなので、簡単に使えるようになっています。
h = { "def" => 2, "ghi" => 1, "abc" => 3 } p h.sort.reverse p h
[実行結果]
[["ghi", 1], ["def", 2], ["abc", 3]] {"def"=>2, "ghi"=>1, "abc"=>3}
以上のように、ハッシュのsortメソッドに続けてreverseメソッドを付けることで、ハッシュのキーの降順でソートできることが分かりました。
reverseは逆という意味です。
値の降順ソートではないことも、よく確認されてください。
また、「p h」の出力結果からreverseメソッドを使用したレシーバのハッシュそのものは、ソートされないままであることも確認できました。
ハッシュを値でsortする
ハッシュを値で昇順sortする
ハッシュの値による昇順ソートを行うには、下記の2つの方法があります。
- ハッシュのsort_byメソッドにブロックを渡し、sort基準をカスタマイズする
- ハッシュのsortメソッドにブロックを渡し、sort基準をカスタマイズする
ハッシュのsort_byメソッドによる値の昇順ソート
まずはハッシュのsort_byメソッドを使ってみましょう。
ハッシュのsort_byメソッドにブロックを引数として渡した場合、ブロック変数にキーと値の2つが渡されます。
そして、ブロックの戻値として値を返すと、その値を基準にソートされます。
値を基準にソートすることは、ハッシュの値同士を比較して昇順に並び替えすることになります。
値が小さいなら先に、値が大きいなら後に並び替えられます。結果、昇順に並び替えられます。
[実行結果]
h = { "def" => 2, "ghi" => 1, "abc" => 3 } p h.sort_by{ | k, v | v } p h
[実行結果]
[["ghi", 1], ["def", 2], ["abc", 3]] {"def"=>2, "ghi"=>1, "abc"=>3}
以上のように、ハッシュの値の昇順にソートされたことが分かります。
なお、ブロックの最後の式が、ブロックの戻値となります。今回の場合は、値vが戻値ですね。
また、sort_byメソッドを使用したレシーバのハッシュ自体はソートされないことも確認できました。
ハッシュのsortメソッドによる値の昇順ソート
次に、ハッシュのsortメソッドにブロックを渡し、sort基準をカスタマイズして、値の昇順にソートする方法を解説します。
sortメソッドにブロックを引数として渡すと、ブロック変数にsort用に比較する対象のハッシュの要素が2つ渡されます。
この2つの要素は [ key, value ] の配列です。
2つの要素を比較するには、一般的に <=> 演算子が用いられます。
例えば、ブロック変数を | a, b | とすると、並び替え用に演算子を a <=> b として用いた場合、aがbより小さい時はaが先に、aがbより大きい時はaが後に並び替えられます。
これを利用し、ハッシュの値の昇順ソートを行うには、a[1] <=> b[1] という比較結果をブロックの戻値にします。
ブロック変数a、bは、[ key, value] の配列でしたので、a[1]、b[1]共にハッシュの値であることに注意してださい。
[実行結果]
h = { "def" => 2, "ghi" => 1, "abc" => 3 } p h.sort{ | a, b | a[1] <=> b[1] } p h
[実行結果]
[["ghi", 1], ["def", 2], ["abc", 3]] {"def"=>2, "ghi"=>1, "abc"=>3}
以上のように、ハッシュの値の昇順にソートされたことが分かります。
また、sortメソッドを使用したレシーバのハッシュ自体はソートされないことも確認できました。
ハッシュを値で降順sortする
ハッシュの値による降順ソートを行うにも、下記の2つの方法があります。
- ハッシュのsort_byメソッドにブロックを渡し昇順ソートした後、reverseメソッドで降順ソートする
- ハッシュのsortメソッドにブロックを渡し、sort基準をカスタマイズする
ハッシュのreverseメソッドによる値の降順ソート
まずはハッシュのsort_byメソッドで昇順ソートした後、revserseメソッドを使って降順ソートしてみましょう。
[実行結果]
h = { "def" => 2, "ghi" => 1, "abc" => 3, "ddd" => 5 } p h.sort_by{ | k, v | v }.reverse p h
[実行結果]
[["ddd", 5], ["abc", 3], ["def", 2], ["ghi", 1]] {"def"=>2, "ghi"=>1, "abc"=>3, "ddd"=>5}
以上のように、reverseメソッドでハッシュの値で降順ソートできることが分かりましたね。
また、reverseメソッドを使用しても、レシーバのハッシュ自体はソートされないことも分かりました。
ハッシュのsortメソッドによる値の降順ソート
次に、ハッシュのsortメソッドにブロックを渡し、sort基準をカスタマイズして、降順にソートする方法を解説します。
sortメソッドにブロックを引数として渡すと、ブロック変数にソート用に比較する対象のハッシュの要素が2つ渡されることは学びましたね。
降順でソートするには、ブロック変数を| a, b | とすると、並び替え用に <=> 演算子を b <=> a として用いた場合、aがbより小さい時はaが後に、aがbより大きい時はaが先に並び替えられます。
これを利用し、ハッシュの値の降順ソートを行うには、b[1] <=> a[1] という比較結果をブロックの戻値にします。
ブロック変数a、bは、[ key, value] の配列でしたので、a[1]、b[1]共にハッシュの値であることに注意してださい。
[実行結果]
h = { "def" => 2, "ghi" => 1, "abc" => 3, "ddd" => 5 } p h.sort{ | a, b | b[1] <=> a[1] }
[実行結果]
[["ddd", 5], ["abc", 3], ["def", 2], ["ghi", 1]] {"def"=>2, "ghi"=>1, "abc"=>3, "ddd"=>5}
以上のように、ハッシュの値の降順にソートされたことが分かります。
また、sortメソッドを使用したレシーバのハッシュ自体はソートされないことも確認できました
ハッシュのsort結果をto_hでハッシュにする
今までRubyのハッシュのsortの実行結果を見てきましたが、sortメソッドやsort_byメソッドの実行結果は、ハッシュではありませんでした。
sortメソッドやsort_byメソッドの実行結果は、操作性や配列との互換性などの理由により、Enumerableというモジュールになっています。
Enumerableモジュールとは、配列と同等の機能を持つモジュールです。
通常は配列と思って頂いてかまいません。
しかし、やはりハッシュのsort結果はハッシュで受け取りたいですよね。
実は、Enumerableにto_hメソッドを適用することで、Enumeableをハッシュに変換できます。
実際にコードで試してみましょう。
h = { "def" => 2, "ghi" => 1, "abc" => 3 } p h.sort.to_h p h.sort.reverse.to_h p h.sort_by{ | k, v | v }.to_h p h.sort{ | a, b | b[1] <=> a[1] }.to_h p h
[実行結果]
{"abc"=>3, "def"=>2, "ghi"=>1} {"ghi"=>1, "def"=>2, "abc"=>3} {"ghi"=>1, "def"=>2, "abc"=>3} {"abc"=>3, "def"=>2, "ghi"=>1} {"def"=>2, "ghi"=>1, "abc"=>3}
以上のように、Enumerableにto_hメソッドを適用することで、ハッシュに変換することができました。
これは便利ですので、この機会に、ぜひ、覚えておいてください。
なお、to_hメソッドは、rubyのver2.1以上で使用可能です。
まとめ
いかがでしたでしょうか?
この記事では、rubyのsortを解説しました。
基本的な並び替えはsortメソッド、sort_byメソッドを使えば実現できることを学びました。
sort_byメソッドではブロック内で処理できるので、より拡張性を持った並び替えができます。
また、ハッシュの場合sortやsort_byでの戻り値は多次元配列となってしまいます。
なので、再びハッシュとして扱いたい場合はto_hメソッドを使えば変換できます。
冒頭で説明したとおり、rubyにはsortの他にもたくさんの便利なメソッドが用意されています。
これを機に勉強してみてはいかがでしょうか?
もしsortについて忘れてしまったらこの記事を確認してくださいね!