ソート(並べ換えること)は、大量のデータ処理には欠かせない。
数値だけが並ぶ配列であれば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"]
降順順、昇順以外の複雑な並べ換えもできる。 たとえば、次のような配列の並べ換えを考える。
score = [ # 内部にハッシュを複数持つ配列 {"氏名" => "山田太郎", "国語"=> 50, "数学"=>40}, {"氏名" => "中町太郎", "国語"=> 90, "数学"=>70}, {"氏名" => "飯森花子", "国語"=> 91, "数学"=>90}, {"氏名" => "鶴岡一人", "国語"=> 60, "数学"=>50} ] score.length # 要素数は4 => 4 # たとえば添字番号2の要素は以下のハッシュ score[2] => {"氏名"=>"飯森花子", "国語"=>91, "数学"=>90}
このようなときに「4つの要素を「国語」キーの値(つまり国語の点)の 高い順に並び換える」ことを考える。
普通の sort
メソッドは各要素どうしをそのまま直接比較して
ソートするが、要素そのままではなくハッシュのキーを取り出した「値」で
比べることを指定する。そのために使うのが sort_by
メソッドで、以下のように用いる。
配列.sort_by{|変数| 変数から比較すべきものを取り出す式}
score 変数の例の場合では、たとえば
x = score[2]
とした場合に、x から国語の点数を取り出すには、
x["国語"]
とすればよいことから、
score.sort_by{|x| x["国語"]}
とすることで国語の点数で昇順にした配列が得られ、さらにこれを
reverse
メソッドに渡して降順の結果が得られる。
score.sort_by{|x| x["国語"]}.reverse
=> [{"氏名"=>"飯森花子", "国語"=>91, "数学"=>90},
{"氏名"=>"中町太郎", "国語"=>90, "数学"=>70},
{"氏名"=>"鶴岡一人", "国語"=>60, "数学"=>50},
{"氏名"=>"山田太郎", "国語"=>50, "数学"=>40}]
このソート結果を順序よく出力するには、for
文で1つずつ取り出して処理する。
for i in score.sort_by{|x| x["国語"]}.reverse ……出力処理…… end
以上をまとめたプログラムを示す。
#!/usr/koeki/bin/ruby # coding: utf-8 require "./kprintf.rb" point = Array.new while yline = gets if /(\S+)\s+(\d+)\s+(\d+)/ =~ yline # 1個目の() (\S+)→氏名が入る # 2個目の() (\d+)→国語の得点が入る # 3個目の() (\d+)→数学の得点が入る j = $2.to_i # 国語の得点 m = $3.to_i # 数学の得点 point << {"name" => $1, "ja" => j, "math" => m, "total" => j+m} # ついでに合計点も入れておく end end print "--氏名--------------+-国語-+-数学-+-合計--\n" for i in point.sort_by{|x| x["ja"]}.reverse student = i["name"] kokugo = i["ja"] math = i["math"] total = i["total"] printf("%-20s %5d %5d %5d\n", student, kokugo, math, total) end puts "-"*42
以下の出力が得られる。
--氏名--------------+-国語-+-数学-+-合計-- 飯森花子 91 90 181 中町太郎 90 70 160 鶴岡一人 60 50 110 酒田三吉 52 80 132 山田太郎 50 40 90 三川一二三 12 75 87 ------------------------------------------
※発展※ →ハッシュ値をまるごとソート