【Ruby】ある与えられた入力を昇順に並び替えて出力する

こんにちは。
早く駆け出したい修行僧です。

今回、paizaラーニング 「背番号順 Ruby編」にて学びがありましたので、
こちらでアウトプットしていきます。
まだ問題を解いていない方は、ここで退出することをお勧めします。

尚、「それは違うよ」など訂正箇所がございましたら、
愛のあるご指摘を頂けたら幸いです。

## :pen_ballpoint:  問題

最初にある数値nが与えられます。
その後、n行の背番号と選手名の文字列が与えられます。
背番号が小さい順に並び替えて出力してください。

## :telescope: コード考察

ここからは一つ一つコードがどのような処理になっていくのか見ていきます。

### 2-1. 最初に数値nを入力できるようにする

```ruby:main.rb
n = gets.to_i
#=> 5
```

まずは、`gets`メソッドを使用してサンプルコードを入力できるようにして、変数nに格納します。また、後に繰り返し処理を行うので、この変数nを数値として扱えるように`to_i`を加えます。`gets`メソッドは1行を読み込んで成功したら文字列を返すようになっているからですね。[[公式リファレンス]](https://docs.ruby-lang.org/ja/latest/method/IO/i/gets.html)
上記の「5」という結果は適当に割り当てた数値です。ここでは「5」が入力された仮定で進めていきます。

尚、今回はn回入力されることが分かっていて、それを並び替えることを考えると空の配列を用意しておくと良いかもしれません。ですので、以下を追加していきます。配列を作るということは要素は複数であると考えられるので変数も複数形で定義しております。

```ruby:main.rb
n = gets.to_i

players = []
```

### 2-2. n行の文字列を入力できるようにする

続いて、最初に与えられた数値を基にその数値行背番号と選手名が与えられるようにしていきます。ここで、登場するのが繰り返し文ですね。

```ruby:main.rb
n.times do
  # 繰り返したい処理
end
```

上記で数値を取得することができたので、`times`メソッドを活用していけば良いですね。
`times`メソッドはself回繰り返します。また、正の数値以外が与えられた場合は何もしません。[[公式リファレンス]](https://docs.ruby-lang.org/ja/latest/method/Integer/i/times.html)
ですので、今回は「5」が入力されたと仮定しているので、上記のコードは実際には以下のようなコードを意味しています。

```ruby:main.rb
5.times do
  # 繰り返したい処理
end
```

そして、この節ではn回入力できるようにしたいので、2-1でも使用した`gets`メソッドを使っていきましょう。

```ruby:main.rb
5.times do
  str = gets.split
end
```

`split`メソッドは第1引数に与えられたセパレータによって文字列の配列を返します。[[公式リファレンス]](https://docs.ruby-lang.org/ja/latest/method/String/i/split.html)
なので、例えば変数`str`に`10 Honda`が入力されたとすると、`["10", "Honda"]`のような文字列の配列が格納されます。

### 2-3. 格納した値を配列に加えていく

変数`str`に格納しただけでは、繰り返し処理の中で更新されていってしまうので、どこかに格納する必要がありそうです。ここで登場するのが2-1で定義した空の配列`players`です。では、コードを見ていきましょう。

```ruby:main.rb
5.times do
  str = gets.split
  players << [str[0].to_i, str[1]]
end
```

まず空の配列に格納するための`<<`は省略した書き方です。これは`push`メソッドを使った場合と同じ機能を実現してくれます。

```ruby:irb
s = ["野球", "サッカー"]

s.push("バレー")
#=> ["野球", "サッカー", "バレー"]
```

では、右辺を見ていきましょう。先ほどまでの処理で`["10", "Honda"]`が変数`str`に格納されていました。ですので、ここでいう`str[0]`は配列のインデックス番号0の値を指します。すなわち`"10"`ですね。そして、`to_i`によって数値としての`10`が空の配列である`players`に格納されるということですね。もうお分かりだと思いますが、`str[1]`は`"Honda"`ですね。

```ruby
[str[0].to_i, str[1]]

str[0].to_i    #=> 10
str[1]         #=> "Honda"
```

2回目以降も同様に格納されていくので、2回目時点では`[[10, "Honda"], [26, "Kagawa"]]`のような結果が得られるかと思います。

### 2-4. 配列`players`を並び替える

続いて、今回の目玉とも言える並び替える処理を見ていきます。昇順で並び替えるときは、`sort`もしくは`sort_by`を使用します。どちらも配列の内容を比較して、昇順に並び替えをしてくれます。両者の違いとしては、比較する条件が複雑な場合における実行速度が上げられます。`sort_by`は複雑な条件においてもメソッドの実行回数は要素数と同じであり実行速度としては速いです。[[公式リファレンス]](https://docs.ruby-lang.org/ja/latest/method/Enumerable/i/sort_by.html)

```ruby:main.rb
players.sort_by!(&:first)
```

`sort_by!`とすることで破壊的に配列`players`を上書きします。カッコ（）の中身ですが、“symbol-to-proc”と呼ばれる省略記法です。実際には以下のようなコードと同じになります。

```ruby:
# 省略した書き方
players.sort_by!(&:first)

# 上記の書き方と意味は同じ
players.sort_by!{ |x| x.first }
```

これによって仮に3回目が`[1, "Kawashima"]`、4回目が`[5, "Nagatomo"]`、5回目が`[14, "Ito"]`が入力されていたとすると、以下の順番にソートされることになるかと思います。

```ruby
players.sort_by!(&:first)
# [1, "Kawashima"] [5, "Nagatomo"] [10, "Honda"] [14, "Ito"] [26, "Kagawa"]
#=> 5
```

### 2-5. 出力するための形に整える

では、最後に期待される出力にするため整えていきます。期待される出力とは`"10 Honda"`のような一つの文字列にまとめて出力することです。繰り返し処理を使ってまとめていけそうですね。

```ruby:main.rb
players.each { |s| puts "#{s[0]} #{s[1]}" }
```

`players`には前節で破壊的にソートを行ったので、背番号が若い順に格納されていました。
それをeach文を利用して、一つ一つの要素（例えば`[5, "Nagatomo"]`）をブロック内の引数sに代入していきます。変数展開で期待される出力と同じ形で記述していきます。`s[0]`はお分かりのとおり一つの要素内のインデックス番号[0]を指します。ですので、背番号に当たる部分です。後半の`s[1]`は選手名ということになります。

## :soccer: まとめ

出てきたのは基本的な文法ばかりでしたが、それを複数組み合わせることで様々な出力をすることができます。目指すゴールをイメージして、それまでの過程をどのように記述していくか言語化することが大事だと実感しました。これからも励んでいきたいと思います。
最後までお読みいただきありがとうございました！

# :page_facing_up: 参考

[公式リファレンス Array#sort](https://docs.ruby-lang.org/ja/latest/method/Array/i/sort.html)
[&:って??? - @soma_sekimoto さん](https://qiita.com/soma_sekimoto/items/a828b8f88b78aac2e7df)
[【Ruby】array.map(&:method)を理解する - @k-penguin-sato さん](https://qiita.com/k-penguin-sato/items/420d7487b28b5d58cac4)