【Ruby】sort()メソッドで配列やハッシュを並び替えてみよう

  • 2021年8月30日
  • 2021年10月16日
  • Ruby

今回は、Rubyで配列などのソートに利用される、sort()メソッドの使い方について解説していきます。

昇順・降順でのソート方法や、似た機能を持つメソッドとの相違点についても解説しましたので、Rubyを使ったソート処理を理解したい人は、ぜひ参考にしてください。

なお、Rubyの魅力や特徴については以下の記事で詳しく解説していますので、こちらもぜひご覧くださいね。

おすすめ記事

みなさんこんにちは! 今回の記事は、 悩みを抱えた人 ・Rubyってどんなプログラミング言語なの?・Rubyを勉強すると将来役に立つかな?・Rubyのおすすめの学習方法が知りたい! というお悩みを解決する記事にな[…]

Rubyとは?その特徴や将来性、おすすめの学習方法まで網羅的にご紹介します

レバテックフリーランス … 業界最大級の案件数業界トップクラスの高単価報酬を誇る最大手のサービスです。実績豊富なコーディネーターが丁寧な対応をしてくれるため、案件の無理な提案はありません。フリーランスで生きていくためにはまず登録しておきましょう。
MidWorks … フリーランス賠償責任保障生命保険の折半など、フリーランスでありながらも正社員並みの保障を受けられるのが特徴です。また、経験豊富なキャリアコンサルタントによる手厚いサポートも受けられるため、安定したフリーランス生活を送りたい方には特におすすめのサービスです。
ポテパンフリーランス … IT業界・技術に詳しいコンサルタントが担当してくれるため、こちらの要望をきちんと理解した上で案件を紹介してくれます。また、案件情報のみならず、フリーランスのイロハについても教えてくれるため、フリーランスとして初めて活動される方には特におすすめのサービスです。

レバテックキャリア … ITエンジニアが利用したい転職エージェントNo.1にも選ばれており、年収600万円以上のハイクラス求人を5,000件以上も保有しています。エンジニアが転職を考えた時にまず初めに登録しておくべきサービスです。
Tech Stars Agent … Tech Stars Agentでは、担当エージェントが全員エンジニア出身のため、スキルやキャリアを見据えたきめ細かな転職支援が受けられます。運営元の株式会社Branding Engineerは、独立支援サービス「MidWorks」も展開しているため、独立を視野に入れたサポートも受けられます
転職ドラフト … 年収UP率93.8%/平均年収UP額126万円と圧倒的な年収UP率を誇るイベント型のエンジニア向け 転職サービスです。毎月1回開催され、厳選された優良IT/Web系企業約150社からダイレクトスカウトを受け取ることができます。年収アップを目指す方は登録必須です。


sort()メソッドの使用方法

sort()メソッドは、配列ハッシュの各要素を比較して、昇順に並べ替える機能を持ったインスタンスメソッドです。

ここでは、配列とハッシュを対象に、sort()メソッドの使い方について確認してみましょう。

配列のソート

まずは、配列から確認しましょう。以下のサンプルコードをご覧ください。

# ソート対象の配列
random_data = [3, -3, 8.4, 34, 8.0, -29, 18, 8.7]

# sort()メソッドを呼び出して昇順にソート
aligned_data = random_data.sort

p aligned_data # [-29, -3, 3, 8.0, 8.4, 8.7, 18, 34]

ソート対象の配列として、小数や負の数を含んだ数値がランダムに格納された配列random_dataを用意しました。

sort()メソッドの使い方は非常に簡単で、配列をレシーバとして呼び出すだけで大丈夫です。

サンプルコード7行目の出力を見ると、小数の部分も含めて、データが値の小さい順に並べ替えられていることが分かります。

数値データ以外にも、文字列の配列をソートすることもできます。以下のサンプルコードをご覧ください。

# ソート対象の文字列配列
random_words = [
  'egg',
  'telephone',
  'teacher',
  'headphone',
  'display',
  'watch',
  'tea',
]

aligned_words = random_words.sort

p aligned_words # ["display", "egg", "headphone", "tea", "teacher", "telephone", "watch"]

2行目の配列random_wordsには、英単語が順不同で格納されています。

これに対してsort()メソッドを呼び出すと、アルファベット順に英単語が再配列されます。

特筆すべきは、頭文字が「te」で共通している「tea」「teacher」「telephone」の整列です。この場合は、次の文字で比較するように動作します。つまり、辞書順に英単語が配置されるということです。

また、英単語以外にも、ひらがなやカタカナ、漢字などもソート可能です。

様々な種類の文字を同時にソートした場合、どのような順番で並べ替えられるのか、以下のサンプルコードで確認しましょう。

# さまざまな種類の文字をソートする場合
random_str = ['B', 'd', 'キ', '7', 'Y', 'あ','a', '1', '玉', '紅', 'え', 'ウ']

aligned_str = random_str.sort

# 数字→英大文字→英小文字→ひらがな→カタカナ→漢字 の順にソート
# 厳密にはUnicodeの文字コードの小さい順にソートされている
p aligned_str # ["1", "7", "B", "Y", "a", "d", "あ", "え", "ウ", "キ", "玉", "紅"]

# 文字コード順に整列されていることを確認する
aligned_str.each { |char|
  # ordメソッドは文字に対応する文字コードを取得する
  p char + ":" + char.ord.to_s
}

上記サンプルコードを実行してみると、数字を先頭にして、アルファベットの大文字から小文字、ひらがなからカタカナ、そして最後に漢字の順番で再配列されていることが分かります。

どうしてこの順番で並べ替えられるのか、ord()メソッドを使用して確認しましょう。

ord()メソッドは、レシーバの文字データに対応する文字コードを取得する機能を持つメソッドです。

each()メソッドで順に確認すると、値の小さい順に出力されることが分かります。文字データをソートする際は、文字データに対応する文字コードを比較するのが基本的な動作となります。

ハッシュのソート

つづいて、ハッシュのソートです。以下のサンプルコードで使い方を確認していきましょう。

# ランダムに格納されたハッシュ
random_hash_data = {
  e: 12,
  i: 54,
  o: 4,
  u: 5,
  a: 6,
}

# ハッシュに対してもsort()メソッドを使用可能
aligned_hash_data = random_hash_data.sort

# ハッシュの場合、キーを基準に整列される
p aligned_hash_data # [[:a, 6], [:e, 12], [:i, 54], [:o, 4], [:u, 5]]

ハッシュデータの場合、キーを基準に並び替え処理が行われます。ハッシュの値を基準にソートする方法については、後節「ハッシュの値を基準にソートする」にて解説します。

ただし、ハッシュデータに対してsort()メソッドを呼び出した場合、戻り値が配列に変化することにも注意してください。

ハッシュとして取り扱いたい場合は、sort()メソッド呼び出し後にto_h()メソッドを呼び出しましょう。

sort!()メソッドとの違い

sort()メソッドは、ソート済みのデータを新たな配列として確保するのに対し、sort!()メソッドは、レシーバ自体を変更する機能を持ちます。

両者でどのように動作が異なるのか、以下のサンプルコードを実行して確認してみましょう。

random_data_1 = [4,3,1,8,2,7,6]
random_data_2 = [4,3,1,8,2,7,6]

return_array_1 = random_data_1.sort
return_array_2 = random_data_2.sort!

# 両メソッド、ともに戻り値は整列済みの配列
p "sort()メソッドの戻り値 ;" + return_array_1.to_s # "sort()メソッドの戻り値 ;[1, 2, 3, 4, 6, 7, 8]"
p "sort!()メソッドの戻り値;" + return_array_2.to_s # "sort!()メソッドの戻り値;[1, 2, 3, 4, 6, 7, 8]"

# sort()呼び出し後、レシーバの中身は変化しない
p "sort()呼び出し後 :" + random_data_1.to_s # "sort()呼び出し後 :[4, 3, 1, 8, 2, 7, 6]"

# sort"()呼び出し後、レシーバの中身は整列済みの配列に変化する
p "sort!()呼び出し後:" + random_data_2.to_s # "sort!()呼び出し後:[1, 2, 3, 4, 6, 7, 8]"

ポイントは、メソッドを呼び出した後のレシーバの中身です。

サンプルコード12行目を見ると、sort()メソッド呼び出し後、レシーバである配列random_data_1の中身は何も変化しません。一方15行目に目を移すと、sort!()メソッドの場合はレシーバである配列random_data_2の中身が、昇順にソートされていることが分かります。

Rubyでは、メソッドを呼び出すことでレシーバの情報が更新されることを「破壊的変更」と呼び、新たにメモリを確保する必要がない場合などに利用するのに最適です。

sort_by()メソッドとの違い

sort_by()メソッドもsort()メソッドと同様、配列やハッシュをソートする機能を持ちます。

sort()メソッドと異なる点として、sort_by()メソッドはソート条件を決定するためにブロックを渡さなければなりません

実際、ブロックを渡す場合と渡さない場合の動作の違いを、以下のサンプルコードで確認してみましょう。

# ソート対象の配列
random_data = [4, -5, 8.2, 33, 8.0, -19, 15, 8.8]

# 単に呼び出してもソートは実行されません
aligned_data = random_data.sort_by

p aligned_data # #<Enumerator: [4, -5, 8.2, 33, 8.0, -19, 15, 8.8]:sort_by>

# ブロックを渡すことでソート処理が正常に実行される
# 今回渡したブロックは、昇順にソートする旨を意味します
aligned_data = random_data.sort_by { |data| data }

p aligned_data # [-19, -5, 4, 8.0, 8.2, 8.8, 15, 33]

サンプルコード7行目を見ると、sort_by()メソッドをブロックを渡さずに呼び出した場合、要素の並べ替えは実行されず、単にEnumeratorオブジェクトが返されることが見て取れます。

正常にソート処理を実行するには、サンプルコード11行目に記したように、ブロックを渡しましょう。ブロックを渡せば、サンプルコード13行目のように、整列済みの配列が作成されます。

また、単純な昇順ソート以外にも、条件を付加した上でソート処理を実施することも可能です。

例えば、頭文字が大文字の英単語と小文字の英単語が混ざった配列に対し、すべて小文字に変換するという条件をブロックとして渡せば、辞書順で英単語を並べ替えることができます。

サンプルコードは以下の通りです。

random_words = [
  'Dog',
  'cat',
  'elephant',
  'Squirrel',
  'Rabbit',
  'horse'
]

# 辞書順に並べ替えようとしても…
aligned_words_1 = random_words.sort_by { |word| word }

# 大文字→小文字の順序が優先され、思うように並べ替えられていない
p aligned_words_1 # ["Dog", "Rabbit", "Squirrel", "cat", "elephant", "horse"]

# すべての要素に対し、downcase()メソッドで小文字化したうえでソート
aligned_words_2 = random_words.sort_by { |word| word.downcase }

# sort_by()メソッドを使えば複雑な条件に対応可能
p aligned_words_2 # ["cat", "Dog", "elephant", "horse", "Rabbit", "Squirrel"]

実は、sort()メソッドでもブロックを渡すことができます。

しかし、sort()メソッドとsort_by()メソッドの間には、ソート処理の速さに差があります。そのため、複雑な条件の下でソート処理を実行する場合は、sort_by()メソッドを利用するのがおすすめです。

sort()メソッドの応用テクニック

ここからは、sort()メソッドの応用的な使い方について解説します。

降順にソートする

sort()メソッドは、基本的には昇順のソートにしか対応していません

降順にソートする方法は、主に2つ存在します。

  • reverse()メソッドを利用する
  • sort()メソッドにブロックを渡す

両者の方法をそれぞれ、以下のサンプルコードに示しました。

# ソート対象の配列
random_data = [13, -3, 4.53, 23, 4.50, -19, 15, 4.6]

# 方法1:reverse()メソッドを呼び出す
aligned_data_1 = random_data.sort.reverse

p aligned_data_1 # [23, 15, 13, 4.6, 4.53, 4.5, -3, -19]

# 方法2:ブロックを渡す
# ブロックの2番目の引数を記号「<=>」の
# 左側に置くことで降順ソートが実行される
aligned_data_2 = random_data.sort { |data1, data2| data2 <=> data1 }

p aligned_data_2 # [23, 15, 13, 4.6, 4.53, 4.5, -3, -19]

いずれの方法も、配列の中身が値の大きいものから小さいものの順に並べ替えられていることが見て取れます。

ハッシュの値を基準にソートする

ハッシュをレシーバとしてsort()メソッドを呼び出した場合、ソートの基準はハッシュのキーです。

もし、ハッシュの値をソートの基準にしたい場合は、sort_by()メソッドを使います。サンプルコードは以下の通りです。

# ランダムに格納されたハッシュ
random_hash_data = {
  e: 12,
  i: 54,
  o: 4,
  u: 5,
  a: 6,
}

# ハッシュの値を基準にソートを実施する場合はsort_by()メソッドを使用しましょう
# 引数keyはハッシュのキー、valueはハッシュの値を指します
aligned_hash_data = random_hash_data.sort_by { |key, value| value }

p aligned_hash_data.to_h # {:o=>4, :u=>5, :a=>6, :e=>12, :i=>54}

サンプルコード12行目で記した、sort_by()メソッドのブロック引数は、それぞれハッシュのキーと値が設定されます。

これにより、ソート処理の基準がハッシュの値に定まり、値の小さい順にハッシュが再配列されます。

複数のキーを基準にソートする

リレーショナルデータベースを利用したアプリケーション開発においては、データベースから取得したデータに対して、複数のキーを基準にソート処理を実施しなければならないことがあります。

単にsort()メソッドを呼び出すだけでは、1次元配列の並び替えしかできませんが、複数条件を記述したブロックを渡すことで、複数のキーを基準にしたソート処理を実装可能です。

以下のサンプルコードをご覧ください。

# [名前, 身長, 体重]の順に
# データが配列で用意されている
physical_data = [
  ['佐藤巧実', 175.4, 58.3],
  ['伊藤亮平', 175.4, 56.9],
  ['高橋秀徳', 175.4, 63.3],
  ['佐久間亮', 172.4, 54.3],
  ['篠原竜也', 176.4, 65.3]
]

HEIGHT = 1 # 身長
WEIGHT = 2 # 体重

aligned_physical_data = physical_data.sort { |d1, d2|
  # 第一条件【身長】を比較
  (d1[HEIGHT] <=> d2[HEIGHT]).nonzero? ||
  # 値が同じ場合は、第二条件【体重】を比較
  (d1[WEIGHT] <=> d2[WEIGHT])
}

aligned_physical_data.each { |data| p data }
# ["佐久間亮", 172.4, 54.3]
# ["伊藤亮平", 175.4, 56.9]
# ["佐藤巧実", 175.4, 58.3]
# ["高橋秀徳", 175.4, 63.3]
# ["篠原竜也", 176.4, 65.3]

サンプルコードでは、名前・身長・体重が組となった2次元配列データに対するソート処理を実装しています。

ソート処理を実施する際、最初は身長を比較し、比較対象の2つのデータが等しい場合は、体重を比較することを条件としています。

身長の比較を行っているのが、サンプルコード16行目の条件式です。もし、両者の値が等しい場合、nonzero?メソッドはnilを返します。そうした場合のみ、サンプルコード18行目の体重比較処理が実行されます。

このように実装することで、複数条件の下のソート処理が実行可能です。

お仕事の途中ですが、少し一休みして、転職独立について考えてみませんか🙌?

現役エンジニアが選ぶおすすめの転職エージェント11選【成功談・失敗談もあります】

レバテックフリーランスの評判ってどう?【現役エンジニアが徹底解説します】

MidWorks(ミッドワークス)の評判ってどう?【現役エンジニアが徹底解説します】

日々の業務に追われて自分を見失わないよう、
定期的にキャリアを振り返るようにしておきましょう🤲

最後に

さて、今回はRubyのsort()メソッドを使って配列やハッシュの要素を並び替える処理について解説してきましたが、いかがでしたか?

使い方自体はシンプルですが、そのままでは、昇順にしか対応していない、ハッシュの場合にはキーでのソートしかできない、などの制約があるため、注意が必要です。

それぞれ、ブロックsort_by()メソッドを使用することで対応自体は可能ですので、そちらの使い方もしっかりマスターしておきましょう。

このブログを通じて少しでも「傍(はた)を楽(らく)にする」ことができていれば嬉しく思います。

最後まで読んで頂きありがとうございました。