sortメソッドによる並べ換え

ソート(並べ換えること)は、大量のデータ処理には欠かせない。 sortメソッドの色々な利用方法に慣れておこう。

以下の説明では分かりやすくするため、ソート対象となる配列(またはハッ シュ)に初期値を代入しているが、実践プログラムではプログラム起動時に 入力されたデータなどを元にその都度変わる。

一般的なソート

たくさんの値が要素として配列に格納されている場合、それらを 昇順にソートしたものを得たいならたんに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"]

数値でも文字列でも昇順に並べ換えられる。降順にしたい場合は ソート規準を決めるブロックを付加する。

x = [9, 20, 10, 15, 8]
y = x.sort{|a, b| b<=>a}
y
 => [20, 15, 10, 9, 8]
foo = ["orange", "apple", "strawberry", "pear"]
bar = foo.sort{|xx, yy| yy<=>xx}
bar
 => ["strawberry", "pear", "orange", "apple"]

上記の

{|a, b| b<=>a}

という部分が「ソート規準ブロック」であり、この意味は

|a, b|

2つのものを比べるぞ。左側のものをaとする。 右側にあるものをbとする。

b<=>a <=> は天秤だ。bを左、aを右に載せるぞ。 左の皿にある方が軽かったら(小さかったら)a, bの順番はそのまま。 右の皿にある方が軽かったら a, b の順番を入れ換えるべし。

この規準に則ると、天秤で比べた左の皿にある(b)の方が小さくなる ように並べ換えるので、結果として元の並びの右側の方が小さくなるように、 つまり降順に並べ換えられることになる。

ちなみに、昇順ソートはソート規準ブロックに {|a, b| a<=>b} を指定したのと同じである。

x = [9, 20, 10, 15, 8]
y = x.sort{|a, b| a<=>b}
y
 => [8, 9, 10, 15, 20]

これは素直に左の皿に載せた(a)の方が小さくなるように並べ換えられる。

独自規準のソート

「大きい順」、「小さい順」より複雑な並べ換えもできる。 たとえば、文字列の「最後の1字」の大小で並べ換えてみよう。 そのためには、

  1. ソート規準ブロックに値がどう渡されるか
  2. 文字列の最後の1字の取り出し方

両方を理解する必要がある。

  1. ソート規準ブロックに値がどう渡されるか

    手作業でものを並べ換えるときでも、基本的には「2つのものの大小を比較」 して、小さい方を左に寄せる、などの作業を積み重ねている。 プログラミング言語でも同様で、どれか2つの値 の大小を比べることの積み重ねで行なっている。

    ソート規準ブロック

    x.sort {|x, y| ....}
    

    の2つの仮引数が「どれか2つの値」を保持するために使われる。 上記の場合変数 x, y に配列の要素のどれかが代入される。

  2. 文字列の最後の1字の取り出し方

    文字列の添字に負の整数を指定すると後から数えた位置の文字を 取り出せる。

    "abcdefg"[-1]
      => 103  (103はgの文字コード)
    

以上のことを踏まえて「文字列の最後の1字の大小でソート」する には以下のようにする。

foo = ["orange", "apple", "strawberry", "pear"]
bar = foo.sort{|xx, yy| xx[-1]<=>yy[-1]}
bar
 => ["orange", "apple", "pear", "strawberry"]

ハッシュのソート

以下のようなハッシュpriceを、値段の安い順に並べ換えたも のを得たい。

price  = {
  "いちご" => 20,   "梨"     => 120,
  "りんご" => 150,  "みかん" => 30,
}

ハッシュをソートするときには key のみを取り出してそれらを 独自規準でソートするという考え方で進める。Hashの もつkeysメソッドは、ハッシュからkeyのみを 抽出した配列を生成する。

price.keys
 => ["りんご", "いちご", "みかん", "梨"]

key一覧、つまり品名一覧が配列の形で返される(順番はprice への代入時の順番とは無関係)。これをsort メソッドで並べ換える。 このとき、並べ換え規準は品名ではなく値段なので、sort メソッ ドのソート規準ブロックでは value どうしを比較する。

price.keys.sort {|x, y| price[x] <=> price[y]}
 => ["いちご", "みかん", "梨", "りんご"]

得られた配列は、値段の小さい順に並んでいるので、この要素を一つずつ取 り出し value とペアにして出力していけばよい。

price  = {
  "りんご" => 150,  "みかん" => 30,
  "いちご" => 20,   "梨"     => 120
}
for item in price.keys.sort{|x, y| price[x] <=> price[y]}
  printf("%s は %d円です。\n", item, price[item])
end

以下の出力が得られる。

いちご は 20円です。
みかん は 30円です。
梨 は 120円です。
りんご は 150円です。

※発展※ →ハッシュ値をまるごとソート


本日の目次