Search

検索したいワードを入力してください

2019年04月10日

Rubyのsortで配列やハッシュを並び替える方法

本記事では、配列やハッシュをソートするsortメソッドについて紹介します。これらは昇順にソートするメソッドですが、reverseメソッドやブロック式を利用すると、降順にソートすることもできます。こちらについても紹介します。

Rubyのsortメソッドとは

Rubyにおけるsortメソッドでは、配列など、データの集まりをある順序で並び替えることができます。メソッドがあるので、ソートアルゴリズムを自分で実装する必要はありません。数値の場合は小さい順に、文字列の場合は、辞書順に並び替えられます。

Rubyのsortの使い方

Rubyにおけるsortメソッドの構文は、オブジェクト.sort{|変数|比較式}です。変数は隣接する2つの要素を表します。また、ブロックは指定しなくても使うことができます。

配列をソートする方法

sortメソッドは、ブロックがない場合は、数値の場合は大きさの昇順、文字列の場合は辞書順にソートされます。以下のRubyスクリプトでは、整数配列numsを大きさの昇順に、文字列配列wordsを辞書順にソートしています。

nums=[54, 77, 48, 38, 79, 92, 39, 8, 91, 21]
words=["Apple","Banana","Kiwi","Orange","Peach","Lemon"]

p nums.sort #=> [8, 21, 38, 39, 48, 54, 77, 79, 91, 92]
p words.sort #=> ["Apple", "Banana", "Kiwi", "Lemon", "Orange", "Peach"]

降順の場合はreverseを使う

Rubyの配列(Array)には、順番を逆転するreverseメソッドがあります。よって、sortメソッドで、昇順にソートした後で、reverseメソッドを使うと、降順にソートした結果が得られます。

nums=[54, 77, 48, 38, 79, 92, 39, 8, 91, 21]
words=["Apple","Banana","Kiwi","Orange","Peach","Lemon"]

p nums.sort.reverse #=> [92, 91, 79, 77, 54, 48, 39, 38, 21, 8]
p words.sort.reverse #=> ["Peach", "Orange", "Lemon", "Kiwi", "Banana", "Apple"]

sort_byメソッドでも同様の処理が可能

数値の場合、sort_byブロック内で符号を反転してからソートを行うという方法でも、降順にソートすることができます。なお、文字列についてはこのメソッドは使えません。

nums=[54, 77, 48, 38, 79, 92, 39, 8, 91, 21]
p nums.sort_by{|n|-n} #=> [92, 91, 79, 77, 54, 48, 39, 38, 21, 8]

ソート速度の比較

数値配列を降順にソートするには、2つの方法がありますが、速度に違いはあるのでしょうか?以下のRubyスクリプトで検証してみます。

require"benchmark"

nums=(1..10_000_000).to_a.shuffle

tsort1=Benchmark.realtime{nums.sort.reverse}
puts"%6.3f(s)"%tsort1 #=> 4.014(s)

tsort2=Benchmark.realtime{nums.sort_by{|n|-n}}
puts"%6.3f(s)"%tsort2 #=> 14.597(s)

puts"%.1f倍"%(tsort2.to_f/tsort1) #=> 3.6倍


reverseメソッドを使う方が約3.6倍高速になりました。sort_byブロック内で、配列の要素を変換するのに時間がかかっているためと考えられます。

ハッシュをキーでソートする

ハッシュについても、sort/sort_byメソッドが提供されています。ハッシュには順番(インデックス)がないので、ソートした結果は、キーと値の組の配列として返されます。

昇順ソートの方法

RubyのHashクラスにおいては、sortメソッドで何も引数を指定しない場合は、ハッシュのキーについてソートされます。

prices={"Apple"=>98,"Banana"=>128,"Kiwi"=>88,"Orange"=>118,"Peach"=>105,"Lemon"=>110}

p prices.sort #=>[["Apple", 98], ["Banana", 128], ["Kiwi", 88], ["Lemon", 110], ["Orange", 118], ["Peach", 105]]

降順ソートの方法

キーの降順にソートする方法については、まずソートした後、reverseで反転させるという方法が考えられます。

prices={"Apple"=>98,"Banana"=>128,"Kiwi"=>88,"Orange"=>118,"Peach"=>105,"Lemon"=>110}

p prices.sort.reverse #=> [["Peach", 105], ["Orange", 118], ["Lemon", 110], ["Kiwi", 88], ["Banana", 128], ["Apple", 98]]

その他に、sortブロック内で比較演算子を使ってもソートを行うことができます。まず、ブロック内に2つ変数を指定します。これらは隣接する左右の要素を表します。添字の0はキー、1は値を表しますので、第1変数を右辺、第2変数を左辺におくことでキーの降順にソートすることができます。

p prices.sort{|a,b|b[0]<=>a[0]} #=> [["Peach", 105], ["Orange", 118], ["Lemon", 110], ["Kiwi", 88], ["Banana", 128], ["Apple", 98]]

ソート速度の比較

ハッシュの降順ソートについて、reverseを使う方法と、sortブロック内で指定する方法とのソート速度の比較を行います。以下のRubyスクリプトでは、ソートをそれぞれ100万回行って時間を計測しています。

require"benchmark"

def measure_time(n_times=1)
Benchmark.realtime do
n_times.times do
yield
end
end
end

prices={"Apple"=>98,"Banana"=>128,"Kiwi"=>88,"Orange"=>118,"Peach"=>105,"Lemon"=>110}
NTIMES=1000_000

#降順ソート
puts"%.3f(s)"%measure_time(NTIMES){prices.sort.reverse} #=> 3.685(s)
puts"%.3f(s)"%measure_time(NTIMES){prices.sort{|a,b|b[0]<=>a[0]}} #=> 3.326(s)

結果はsortメソッドでブロックを使うほうがやや高速になりました。ソート結果は、キーと値の組からなる配列であり、逆転させるのに時間がかかるためと考えられます。

ハッシュを値でソートする

ハッシュは値についてもソートすることができます。これには、sortメソッドとsort_byメソッドを使う方法があります。

まず、sortメソッドを使う場合、ブロックを用いてsort{|a,b|a[1]<=>b[1]}とします。

次に、sort_byメソッドを使う場合、ブロック内で変数を2つ指定すると、キーと値を表します。よって、sort_by{|k,v|v}とすれば、値についてソートされます。

昇順ソートの方法

以下のRubyスクリプトでは、sort/sort_byメソッドを使って、ハッシュの値についてソートしています。

prices={"Apple"=>98,"Banana"=>128,"Kiwi"=>88,"Orange"=>118,"Peach"=>105,"Lemon"=>110}

p prices.sort{|a,b|a[1]<=>b[1]}
p prices.sort_by{|k,v|v}

#結果はいずれも
[["Kiwi", 88], ["Apple", 98], ["Peach", 105], ["Lemon", 110], ["Orange", 118], ["Banana", 128]]

ソート速度の比較

これら2つの方法について、速度の比較を行います。時間計測には、前と同じmeasure_timeメソッドを使用しています。

NTIMES=1000_000

puts"%.3f(s)"%measure_time(NTIMES){ prices.sort{|a,b|a[1]<=>b[1]} } #=> 2.967(s)
puts"%.3f(s)"%measure_time(NTIMES){ prices.sort_by{|k,v|v} } #=> 2.583(s)


結果は、sort_byメソッドを使う方がやや高速となりました。sort_byでは要素数の分ブロックが実行されますが、sortメソッドでは、要素の比較のたびにブロックが実行されるためと考えられます。

降順ソートの方法

これには3つの方法があります、sortメソッドを使う場合、ブロックを用いてsort{|a,b|b[1]<=>a[1]}とします。sort_byメソッドでは、sort_by{|k,v|-v}で符号を反転、もしくは、sort_by{|k,v|v}.reverseでソート結果を逆転させることで降順ソートできます。

prices={"Apple"=>98,"Banana"=>128,"Kiwi"=>88,"Orange"=>118,"Peach"=>105,"Lemon"=>110}

p prices.sort{|a,b|b[1]<=>a[1]}
p prices.sort_by{|k,v|-v}
p prices.sort_by{|k,v|v}.reverse

#結果はいずれも
[["Banana", 128], ["Orange", 118], ["Lemon", 110], ["Peach", 105], ["Apple", 98], ["Kiwi", 88]]

ソート速度の比較

これら3つの方法について、速度の比較を行います。時間計測には、前と同じmeasure_timeメソッドを使用します。

NTIMES=1000_000
puts"%.3f(s)"%measure_time(NTIMES){ prices.sort{|a,b|b[1]<=>a[1]} } #=> 3.016(s)
puts"%.3f(s)"%measure_time(NTIMES){ prices.sort_by{|k,v|-v} } #=> 2.717(s)
puts"%.3f(s)"%measure_time(NTIMES){ prices.sort_by{|k,v|v}.reverse } #=> 2.918(s)


結果は、sort_byメソッドで符号反転する方法が最速となりました。reverseメソッドではソート結果の配列の逆転に時間がかかり、sortメソッドではブロックで要素を比較する回数が多くなるためと考えられます。

適切なソート方法を選ぼう

Rubyにおけるソートにはsortメソッドとsort_byメソッドがあり、ブロックを使用する方法もあります。これらの違いを表にまとめると、以下のようになります。

ブロックなしブロックあり
sort昇順にソート隣接する2要素を比較、低速
sort_by-各要素を変換してソート、高速

降順ソートの場合は、sortした後にreverseする、sortブロック内で符号反転させる、もしくはsort_byブロック内で符号を反転させるという方法があります。

このように、ソートする方法には複数の方法があるものもあり、速度の違いがあります。ソートの種類によって、最適な方法を選択しましょう。

多くの人がプログラミングを諦めてしまう理由をご存知ですか?



近年プログラミングを勉強する人が増えています。

プログラミング学習者の多くは独学から取り組もうとしますが、だいたい80%ほどは3ヶ月も続かずに諦めてしまいます。早い人は1日目で。

多くの人がプログラミングを独学しようとして諦める理由は、次の3つ。
●モチベーションが維持できない
●エラーの原因・解決方法が分からない
●どう学習すればよいか分からない

TechBoostというプログラミングスクールでは、みんなと一緒にプログラミングをするのでモチベーションの維持ができ、分からないことがあればマンツーマンで教えてくれ、徹底的に研究された初心者向けの教材が揃っています。

TechBoostを卒業後、実際にエンジニアとして転職した方もいるほど。

本気でプログラミングを学びたい方は、一度無料のカウンセリングでご相談ください。プログラミングを嫌いになる前に。

tech boost卒業生インタビュー

tech boostの卒業生の声を聞きました。あなたがプログラミングを学びたい理由を、一度考えてみてください。
営業→Javaエンジニア→Rubyエンジニアと転向し、第一志望のFinTech企業で働く山下さん
元営業、ビジネスのわかるエンジニアを目指す菅原さん
サンフランシスコに交換留学し、シリコンバレーのVCでインターン中の梅本さん
予備校の営業から半年でエンジニア転職を果たした小田島さん

tech boostの口コミ



Related