4
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Array#sort わからないんです。そんな私が調査してみました。

Posted at

Array#sort ってややこしいですよね、イメージがわかないというか。(私だけ?) ということで、ちょこっと触って、それなりに理解できたのでメモ。 大まかにいうと、a <=> b とした値が-1なら a を先に、1ならbを先にするということらしいです。 以下、調査した時のスクリプト

sort.rb
# coding : windows-31j
# 0 から 1000 までのランダムな整数を10個もつ配列を生成
arr = 10.times.collect do |i|
  rand(1000)
end

puts "Random Integers"
p arr

# Array#sort は
# 要素1 <=> 要素2 の結果が
# -1 なら 要素1が先
#  0 なら 同じ
#  1 なら 要素2が先
# となる。
# <=> は オブジェクトによって再定義可能なメソッドであるため
# -1, 0, 1 を返す条件はオブジェクトに依存するが、基本的には
# 要素1が大きければ1 等しければ0, 要素2が大きければ-1を
# 返すように作ることが期待されている。

# 昇順ソート
asc_arr = arr.sort do |a, b|
  a <=> b
end

puts "Ascending Sort"
p asc_arr

# 降順ソート
# -1 : bを先にaを後にする
#  0 : 交換しない
#  1 : aを先にbを後にする
desc_arr = arr.sort do |a, b|
  b <=> a
end

puts "Descending Sort"
p desc_arr

# ブロックを渡さずとも、デフォルトで昇順ソートになる。
puts "Default Sort(Ascending Sort)"
p (arr.sort)

# ブロックを渡すと便利な場合として、テーブルのソートがある
# カラムにID番号を整数で格納するid と 何かの名前を文字列で格納するnameを持った
# クラスが配列にランダムに格納されていたとする。
class AnyRecord
  attr_accessor :id, :name
  def initialize
    @id = create_id
    @name = create_name
  end

  def create_id
    rand(1000)
  end

  def create_name
    # ランダムな文字列生成
    a = ('a'..'z').to_a + ('A'..'Z').to_a + ('0'..'9').to_a
    (
      Array.new(16) do
        a[rand(a.size)]
      end
    ).join
  end
  
  def to_s
    "id:#{@id} name:#{@name}"
  end
end

table = 10.times.map do |i|
  AnyRecord.new
end

puts "Random Records"
table.each { |r| p r }

# ID で昇順ソート
sorted_by_id_table = table.sort do |a, b|
  a.id <=> b.id
end

puts "Sorted by id Records"
sorted_by_id_table.each { |r| p r }

# name で昇順ソート
sorted_by_name_table = table.sort do |a, b|
  a.name <=> b.name
end

puts "Sorted by name Records"
sorted_by_name_table.each { |r| p r }

# ちなみに、引数なしでソートしようとするとエラーになる
# table.sort #=> comparison of Any Record with AnyRecord faild (ArgumentError)
# これはAnyRecord に 比較演算子 <=> が定義されておらず、比較できない為に発生する
# AnyRecord に 比較演算子 <=> を定義してやることで問題は解決する。
class AnyRecord
  # ここでは、idでのソートをデフォルトソート(ブロックなしソート)とする
  def <=>(other)
    self.id <=> other.id
  end
end

sorted_by_default_table = table.sort

puts "Sorted by default Records"
sorted_by_default_table.each { |r| p r }

# 複数のキーでソートする場合は以下のようにする
# nonzero? で nil になると、次のor節が評価される.
puts "Sorted by multi keys"
table.sort { |a, b|
  (a.id <=> b.id).nonzero? ||
    (a.name <=> b.name)
}.each { |r| p r }
4
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?