同じ枚数の人を同じ順位にする（2通りのやり方）

## やりたいこと
カード枚数が同じ枚数だったら同じ順位にしたい。
```ruby
array = [{:name=>"player3", :count=>20},
 {:name=>"player2", :count=>16},
 {:name=>"player1", :count=>16},
 {:name=>"player4", :count=>0}]
 player3が1位、player2が2位、player1が3位、player4が4位です。
```
以下のように出力したい。
```ruby
[{:name=>"player3", :count=>20},
 {:name=>"player2", :count=>16},
 {:name=>"player1", :count=>16},
 {:name=>"player4", :count=>0}]
 player3が1位、player2が2位、player1が2位、player4が4位です。
```

## 考えたこと
「`each_with_index`メソッドで順番に比較して、値が同じなら同じ順位になるように設定できるのではないか？」
「初期値を1としたranking変数を用意して、1つ前と比べて大きければ+1するようにすれば同じことを実現できるのではないか？」

## 試したこと

### やり方①
[前提]
・ここまでに配列の要素は降順にしてある。  
・カード枚数が多い人から高い順位となる。

・先頭の人は順位が`インデックス+1`位となる。(1位)
・2番目以降の人は、1つ前の人のカード数より少ない場合は、順位が`インデックス+1`位となる。
例：配列で3番目にあって、1つ前より少なければ、`2+1`で3位になる。
・1つ前の人のカード枚数と同じであれば、インデックス番号がそのまま順位になる。
例：配列で3番目にあって、1つ前と同数ならば、インデックス`2`だから2位になる。

### 途中経過
・前の人とカード枚数が違う場合と同じ場合で分けた。
・2行目の`if i < n - 1`は、「player4が1位、」「player2が3位、」は`print`メソッドで、最後の要素だけは`puts`で「player5が5位です。」と出力するため。
・カード枚数が同じ場合の処理はまだ書けていないが、条件ごとに分けるところまで。

```ruby
array.each_with_index do |player, i|
  if i < n - 1
    # カード枚数が前の人と違う場合
    if array[i][:count] != array[i - 1][:count]
      print "#{player[:name]}が#{i + 1}位、"
    else # カード枚数が同数の場合
      next if i == 0
      puts 'hi'
    end
  else 
    puts "#{player[:name]}が#{i + 1}位です。"
  end
end
```

```ruby
[{:name=>"player4", :count=>15},
 {:name=>"player3", :count=>15},
 {:name=>"player2", :count=>10},
 {:name=>"player1", :count=>10},
 {:name=>"player5", :count=>0}]
player4が1位、hi
player2が3位、hi
player5が5位です。
```

`puts 'hi'`を置いて、コードがどういう順番で実行されているのか確認しながら進めた。
あとは`'hi'`のところに2位と4位が表示されるようにすれば良さそう。
先述のとおり、1つ前の人のカード枚数と同じであれば、インデックス番号がそのまま順位になるようにした。
```ruby
array.each_with_index do |player, i|
  if i < n - 1
    # カード枚数が前の人と違う場合
    if array[i][:count] != array[i - 1][:count]
      print "#{player[:name]}が#{i + 1}位、"
    else # カード枚数が同数の場合
      next if i == 0
      print "#{player[:name]}が#{i}位、"
    end
  else 
    puts "#{player[:name]}が#{i + 1}位です。"
  end
end
```
その結果、同じカード枚数の場合は同じ順位になった。
3番目のplayer3は、1つ前のplayer4とカード枚数が同じなので、インデックス`2`から2位となる。　　
その後のplayer2は、1つ前のplayer3とカード枚数が違うので、インデックス`3`+1で4位となる。　　
```ruby
[{:name=>"player1", :count=>20},
 {:name=>"player4", :count=>16},
 {:name=>"player3", :count=>16},
 {:name=>"player2", :count=>0}]
player1が1位、player4が2位、player3が2位、player2が4位です。
```

###  失敗例
4行目の`!=`を`<`にすると失敗する。
1位が出力されなくなる。

```ruby
array.each_with_index do |player, i|
  if i < n - 1
    # != を < にするとうまくいかない
    if array[i][:count] < array[i - 1][:count]
      print "#{player[:name]}が#{i + 1}位、"
      print 'hello'
    else
      next if i == 0
      print "#{player[:name]}が#{i}位、"
    end
  else 
    puts "#{player[:name]}が#{i + 1}位です。"
  end
end
```

```ruby
player1が2位、helloplayer3が3位です。
[{:name=>"player2", :count=>36},
 {:name=>"player1", :count=>15},
 {:name=>"player3", :count=>0}]
```

理由は、`i == 0`の場合に、4行目の`array[i - 1][:count]`が`array[-1][:count]`となり、配列の最後の人のカード枚数となってしまうから。  
配列の要素は降順になっており、先頭の人の枚数が最後の人の枚数より少ない場合はないので、`i == 0`の場合に順位をつける処理が実行されない。


### やり方②

初期値を1としたranking変数を用意して、2つの要素を比較した結果によって更新されるようにする。

### 失敗例

```ruby
array.each_with_index do |player, i|
  
  # nilにならない間処理が続く
  if i < n -1
    player[:ranking] = 1
    # 2つ比べて枚数が少ない方はランキング変数が1つ増える
    if array[i][:count] > array[i + 1][:count]
      array[i + 1][:ranking] += 1
    elsif array[i][:count] < array[i + 1][:count]
      array[i][:ranking] += 1
    end

    if i < n - 1
      print "#{player[:name]}が#{player[:ranking]}位、"
    else 
      puts "#{player[:name]}が#{player[:ranking]}位です。"
    end

  end
  
end
```

```ruby
war_step2.rb:248:in `block in <main>': undefined method `+' for nil (NoMethodError)

      array[i + 1][:ranking] += 1
```
`nil`に対して`+`は使えない。

エラーが出たコードの1行上に`p array[i + 1]`を追加して、`array[i + 1]`の中身を確認する。

```ruby
{:name=>"player3", :count=>21}
```
`:ranking`キーがないのに1を足そうとしているからエラーになったと考えられる。

`:ranking`キーに初期値「1」を指定すると、エラーが解消した。

```ruby
array[i + 1][:ranking] = 1
array[i + 1][:ranking] += 1
```

```ruby
{:name=>"player3", :count=>21, :ranking=>1}
evening
player2が1位、{:name=>"player1", :count=>0, :ranking=>1}
evening
player3が1位、戦争を終了します。
```

### 途中経過1
やり方①と同じ条件分岐にして、どのように出力されるか確認してみる。
```ruby
array.each_with_index do |player, i|
  # :rankingに初期値を設定
  player[:ranking] = 1
  
  if i < n - 1

    if i != 0
      # 1つ前の人とカード枚数が同じの場合
      if array[i][:count] == array[i - 1][:count]
        array[i][:ranking] = array[i][:ranking]
      #1つ前の人よりカード枚数が多い場合(不要)
      elsif array[i][:count] > array[i - 1][:count] 
        array[i - 1][:ranking] += 1
      #1つ前の人よりカード枚数が少ない場合
      elsif array[i][:count] < array[i - 1][:count]
        array[i][:ranking] += 1
        puts "hello"
      end
    end

    print "#{player[:name]}が#{player[:ranking]}位、"
  else 
    puts "#{player[:name]}が#{player[:ranking]}位です。"
  end
  
end

pp array
```
そもそもこの配列は降順にしてあるので、1つ前の人よりカード枚数が多い場合の処理は不要であると気づき削除した。

```ruby
player3が1位、hello
player4が2位、hello
player5が2位、player2が1位、player1が1位です。
[{:name=>"player3", :count=>30, :ranking=>1},
 {:name=>"player4", :count=>20, :ranking=>2},
 {:name=>"player5", :count=>0, :ranking=>2},
 {:name=>"player2", :count=>0, :ranking=>1},
 {:name=>"player1", :count=>0, :ranking=>1}]
```

### 途中経過2
1位以外の人が全員2位になってしまった。
```ruby
array.each_with_index do |player, i|
  player[:ranking] = 1
  
  if i != 0
    if array[i][:count] == array[i - 1][:count]
      array[i][:ranking] = array[i][:ranking]
      print "#{player[:name]}が#{player[:ranking]}位、"
      if i == n - 1
        puts "#{player[:name]}が#{player[:ranking]}位です。"
      end
    elsif array[i][:count] < array[i - 1][:count]
      array[i][:ranking] += 1
      print "#{player[:name]}が#{player[:ranking]}位、"
      if i == n - 1
        puts "#{player[:name]}が#{player[:ranking]}位です。"
      end
    end
  else
    print "#{player[:name]}が#{player[:ranking]}位、"
  end
  
end
```

```ruby
player3が1位、player4が2位、player1が2位、player2が2位、player2が2位です。
[{:name=>"player3", :count=>28, :ranking=>1},
 {:name=>"player4", :count=>16, :ranking=>2},
 {:name=>"player1", :count=>8, :ranking=>2},
 {:name=>"player2", :count=>0, :ranking=>2}]
```

1位以外が2位にしかならない原因は、2行目の`player[:ranking] = 1`で毎回1にしてしまっているから。
1つの前の要素の順位が次の要素に引き継がれていないので、12行目の`array[i][:ranking] += 1`が毎回`1 + 1`で2になってしまう。

### 途中経過3（失敗）
2位以下の順位が正しく出力されるように修正した。
1つ前の人よりカード枚数が少ない場合、1つ前の人の順位に+1する。
`array[i][:ranking] = array[i - 1][:ranking] + 1`

```ruby
array.each_with_index do |player, i|
  player[:ranking] = 1
  if i < n - 1
    if i != 0
      if array[i][:count] == array[i - 1][:count]
        array[i][:ranking] = array[i - 1][:ranking]
        print "#{player[:name]}が#{player[:ranking]}位、"
      elsif array[i][:count] < array[i - 1][:count] 
        array[i][:ranking] = array[i - 1][:ranking] + 1
        print "#{player[:name]}が#{player[:ranking]}位、"
      end
    else
      print "#{player[:name]}が#{player[:ranking]}位、"
    end
  else
    if array[i][:count] == array[i - 1][:count]
      array[i][:ranking] = array[i][:ranking]
      puts "#{player[:name]}が#{player[:ranking]}位です。"
    elsif array[i][:count] < array[i - 1][:count]
      # 1つ前の要素の順位に+1する
      array[i][:ranking] = array[i - 1][:ranking] + 1
      puts "#{player[:name]}が#{player[:ranking]}位です。"
    end
  end
  
end
```

同じ順位が何人かいた場合に、その次の人の順位が間違ってしまう。

```ruby
player4が1位、player5が2位、player2が2位、player1が2位、player3が3位です。
[{:name=>"player4", :count=>20, :ranking=>1},
 {:name=>"player5", :count=>10, :ranking=>2},
 {:name=>"player2", :count=>10, :ranking=>2},
 {:name=>"player1", :count=>10, :ranking=>2},
 {:name=>"player3", :count=>0, :ranking=>3}]
```

### 途中経過4（成功）
1つ前の人よりカード枚数が少ない場合、自分よりカード枚数が多い人の数をカウントする。
（単純に自分より前にある要素数を取得すればよかったかもしれない...）

```ruby
if array[i][:count] == array[i - 1][:count]
  array[i][:ranking] = array[i][:ranking]
  puts "#{player[:name]}が#{player[:ranking]}位です。"
elsif array[i][:count] < array[i - 1][:count]
  # 1つ前の要素の順位に+1する
  # array[i][:ranking] = array[i - 1][:ranking] + 1
  
  # その要素より大きい数の要素数をカウントする
  num = array.count do |x|
    x[:count] > array[i][:count]
  end
  
  array[i][:ranking] = num + 1
  puts "#{player[:name]}が#{player[:ranking]}位です。"
end
```
```ruby
player2が1位、player5が2位、player4が3位、player1が3位、player3が5位です。
[{:name=>"player2", :count=>25, :ranking=>1},
 {:name=>"player5", :count=>15, :ranking=>2},
 {:name=>"player4", :count=>5, :ranking=>3},
 {:name=>"player1", :count=>5, :ranking=>3},
 {:name=>"player3", :count=>0, :ranking=>5}]
```

### 完成したコード
重複が多いので、クラスやインスタンスを使って整理して書けるようになりたい。
```ruby
array.each_with_index do |player, i|
  player[:ranking] = 1
  if i < n - 1
    if i != 0
      if array[i][:count] == array[i - 1][:count]
        array[i][:ranking] = array[i - 1][:ranking]
        print "#{player[:name]}が#{player[:ranking]}位、"
      elsif array[i][:count] < array[i - 1][:count]
        # その要素より大きい数の要素数をカウントする
        num = array.count do |x|
          x[:count] > array[i][:count]
        end
        array[i][:ranking] = num + 1

        print "#{player[:name]}が#{player[:ranking]}位、"
      end
    else
      print "#{player[:name]}が#{player[:ranking]}位、"
    end
  else
    if array[i][:count] == array[i - 1][:count]
      array[i][:ranking] = array[i][:ranking]
      puts "#{player[:name]}が#{player[:ranking]}位です。"
    elsif array[i][:count] < array[i - 1][:count]
      # その要素より大きい数の要素数をカウントする
      num = array.count do |x|
        x[:count] > array[i][:count]
      end
      array[i][:ranking] = num + 1

      puts "#{player[:name]}が#{player[:ranking]}位です。"
    end
  end
  
end
```

## 学んだこと
・`next`を使うと条件を満たす場合に処理を中断して次の処理が実行される。
・やり方①は`puts 'hi'`の出力結果を見てから思いついた。詰まったときは`puts 'hi'`などでどのように処理されるか確かめてみると打ち手が見えてくる可能性がある。