ソート(並べ換えること)は、大量のデータ処理には欠かせない。
数値だけが並ぶ配列であればsortメソッドでソートできるが、
複雑な配列やハッシュを様々な条件で並べ換えるときに有用な
sort_by の利用方法に慣れておこう。
以下の説明では分かりやすくするため、 ソート対象となる配列(またはハッシュ)に初期値を代入しているが、 実践プログラムではプログラム起動時に 入力されたデータなどを元にその都度変わる。
たくさんの値が要素として配列に格納されている場合、それらを
昇順にソートしたものを得たいならたんにsortメソッドを
呼ぶだけでよい。
x = [9, 20, 10, 15, 8] y = x.sort y => [8, 9, 10, 15, 20] x = ["orange", "apple", "strawberry", "pear"] y = x.sort y => ["apple", "orange", "pear", "strawberry"]
数値でも文字列でも昇順に並べ換えられる。降順にしたい場合は
結果を reverse で反転すればよい。
x = [9, 20, 10, 15, 8] y = x.sort.reverse y => [20, 15, 10, 9, 8] foo = ["orange", "apple", "strawberry", "pear"] bar = foo.sort.reverse bar => ["strawberry", "pear", "orange", "apple"]
降順順、昇順以外の複雑な並べ換えもできる。 たとえば、次のようなCSVデータの並べ換えを考える。
氏名,国語,数学 山田太郎,50,40 中町太郎,90,70 飯森花子,91,90 鶴岡一人,60,50 酒田三吉,52,80 三川一二三,12,96
このCSVファイルを
score = CSV.read("ja-math.csv", headers:true)
として読み込むと概念的に以下のような構造で読み込まれる(実際には CSV::Tableの値だがハッシュの配列と同じ性質を持つ)。
score = [			# 内部にハッシュを複数持つ配列
  {"氏名" => "山田太郎",	"国語"=> 50, "数学"=>40},
  {"氏名" => "中町太郎",	"国語"=> 90, "数学"=>70},
  {"氏名" => "飯森花子",	"国語"=> 91, "数学"=>90},
  {"氏名" => "鶴岡一人",	"国語"=> 60, "数学"=>50},
  {"氏名" => "酒田三吉",	"国語"=> 52, "数学"=>80},
  {"氏名" => "三川一二三",	"国語"=> 12, "数学"=>96},
]
score.length			# 要素数は6
 => 6
# たとえば添字番号2の要素は "飯森花子" 行の値となる
このようなときに「6つの要素を「国語」キーの値(つまり国語の点)の 高い順に並び換える」ことを考える。
普通の sort メソッドは各要素どうしをそのまま直接比較して
ソートするが、要素そのままではなくハッシュのキーを取り出した「値」で
比べることを指定する。そのために使うのが sort_by
メソッドで、以下のように用いる。
配列.sort_by{|変数| 変数から比較すべきものを取り出す式}
score 変数の例の場合では、たとえば
x = score[2]
とした場合に、x から国語の点数を取り出すには、
x["国語"].to_i とすればよいことから、
score.sort_by{|x| x["国語"].to_i}
とすることで国語の点数で昇順にした配列が得られ、さらにこれを
reverse メソッドに渡して降順の結果が得られる。
score.sort_by{|x| x["国語"].to_i}.reverse
 =>
 [#<CSV::Row "氏名":"飯森花子" "国語":"91" "数学 ":"90">,
  #<CSV::Row "氏名":"中町太郎" "国語":"90" "数学":"70">,
  #<CSV::Row "氏名":"鶴岡一人" "国語":"60" "数学":"50">,
  #<CSV::Row "氏名":"酒田三吉" "国語":"52" "数学":"80">,
  #<CSV::Row "氏名":"山田太郎" "国語":"50" "数学":"40">,
  #<CSV::Row "氏名":"三川一二三" "国語":"12" "数学":"96">]
このソート結果を順序よく出力するには、each
で1つずつ取り出して処理する。
score.sort_by{|x| x["国語"].to_i}.reverse.each do |i|
  ……出力処理……
end
以上をまとめたプログラムを示す。
#!/usr/bin/env ruby # coding: utf-8 require "./kprintf.rb" require 'csv' # ja-math.csv: 氏名,国語,数学 score = CSV.read("ja-math.csv", headers:true) puts "元の並び:" print "--氏名--------------+-国語-+-数学-+-合計--\n" score.each do |row| name, ja, math = row["氏名"], row["国語"], row["数学"] total = ja.to_i + math.to_i printf("%-20s %5d %5d %5d\n", name, ja.to_i, math.to_i, total) end puts "-"*42 puts "国語上位から:" print "--氏名--------------+-国語-+-数学-+-合計--\n" score.sort_by{|x| x["国語"].to_i}.reverse.each do |row| name, ja, math = row["氏名"], row["国語"], row["数学"] total = math.to_i + ja.to_i printf("%-20s %5d %5d %5d\n", name, math.to_i, ja.to_i, total) end puts "-"*42
以下の出力が得られる。
./kokugo-rank.rb
元の並び:
--氏名--------------+-国語-+-数学-+-合計--
山田太郎                50     40     90
中町太郎                90     70    160
飯森花子                91     90    181
鶴岡一人                60     50    110
酒田三吉                52     80    132
三川一二三              12     96    108
------------------------------------------
国語上位から:
--氏名--------------+-国語-+-数学-+-合計--
飯森花子                90     91    181
中町太郎                70     90    160
鶴岡一人                50     60    110
酒田三吉                80     52    132
山田太郎                40     50     90
三川一二三              96     12    108
------------------------------------------
※発展※ →ハッシュ値をまるごとソート