「Ruby初心者向けのプログラミング問題を集めてみた」の電話帳問題解いてみた。

## はじめに
この記事は「Ruby初心者向けのプログラミング問題を集めてみた」の電話帳問題を解く過程から、先輩のレビューをいただき、振り返るところまでを纏めた記事です。
備忘録・振り返り的な要素が強い為、フランクな書き方をしておりますが、大目に見ていただければと思います。軽い読み物だと思って読んでください。
また解き方に稚拙な箇所もあるかと思いますがご容赦ください。指摘等は大歓迎です。励みになります。


## 登場人物
- 私
  - 社会人２年目PG。Rubyでコーディングし始めて１年とちょっと。うっかりポンコツ。最近Ruby Goldを取得した。
- ラテ太郎（アイコン参照）
  - 私の心の中に住んでいる妖精。 ~~白黒つけない~~ いいやつ。ゆるい見掛けによらずしっかりしている。最後の砦。
- タピオカ先輩
  - ラテ太郎の先輩妖精。コーディングが得意。
 

## フェーズ１　「考える・自力で解く」
### 実際に出題された問題
```
# NameIndex

## 問題

- カタカナ文字列の配列を渡すと、ア段の音別にグループ分けした配列を返すプログラムを作成せよ。
- 各要素は 50 音順にソートもすること。

## 例

- IN: `['キシモト', 'イトウ', 'ババ', 'カネダ', 'ワダ', 'ハマダ']`
- OUT: `[ ['ア', ['イトウ']], ['カ', ['カネダ', 'キシモト']], ['ハ', ['ハマダ', 'ババ']], ['ワ', ['ワダ']] ]`

## 提出時、以下全ての条件を満たしていること

- `RSpec` でエラーが発生していないこと
- `RuboCop` で警告が出ていないこと
- `Coverage` が `100%` であること

```

### 考えたこと
私「なるほどなるほど、 `辞書みたい` な感じでそれぞれの名前を纏めてあげればいいんだ。 `out` の形ってなんだか `group_by` した時の形に似てない？（※この時の私は空前の `group_by` ブーム） `同じ感じ` でやれたらいいのにな〜。」

ラテ「こんなイメージで合ってるかな？」

```ruby

class NameIndex
  def self.create_index(names)
    names.group_by { |name| name.chr }
  end
end
```

私「そうそう、そんな感じ。私は `group_by` のところを `group_by(&:chr)` って書くかな。（※書かないとRuboCopに怒られる。）」

```ruby

class NameIndex
  def self.create_index(names)
    names.group_by(&:chr)
  end
end

names = ['キシモト', 'イトウ', 'ババ', 'カネダ', 'ワダ', 'ハマダ']

NameIndex.create_index(names)
=>{"キ"=>["キシモト"], "イ"=>["イトウ"], "バ"=>["ババ"], "カ"=>["カネダ"], "ワ"=>["ワダ"], "ハ"=>["ハマダ"]}
```
私「これを `to_a` で配列にしたらイメージに近くない？なんかイメージに近い気がする！……でもこれって並び替えも必要だよね。うーん、 `各要素はソートすること` って書いてあったよなぁ。各要素をソートする……。」

※シンキングタイム

私「最初から配列の中身ソートしとけばええやんけ…………………………………………………………………………。」

ラテ「気付くのおっそ…………。」

```ruby

class NameIndex
  def self.create_index(names)
    names.sort.group_by(&:chr).to_a
  end
end

names = ['キシモト', 'イトウ', 'ババ', 'カネダ', 'ワダ', 'ハマダ']

NameIndex.create_index(names)
=>[["イ", ["イトウ"]], ["カ", ["カネダ"]], ["キ", ["キシモト"]], ["ハ", ["ハマダ"]], ["バ", ["ババ"]], ["ワ", ["ワダ"]]]
```

私「これです！！！！！！！！！！！！」
私「ちょっとどうしよう凄いしっくりきた。」
私「初めて一行で書けたのでは？」
私「やった〜〜〜〜〜〜〜〜〜〜〜嬉しい！」
私「RSpec書いて、時間を置いてから見直して、リファクタリングできるところあったらリファクタリングしよ！」

・
・
・
・
・
・
・
・



ラテ「お判りいただけただろうか…………。」

ラテ「こいつは凄い愚かなヤツです。どの辺が愚かかというと、 `自分もやればできるじゃん` という気持ちに慢心しているところが愚かなのです。見直しは重要だということは小学生でも知っているのです。勿論、こいつにもそういう気持ちはあったのだと思うのです。あるからこそ `時間を置いてから見直して` なんて言ってるのです。けれど、時間を置きすぎてはならないのです。なぜって時間には限りがあるから…………。」

・
・
・
・
・
・
・
・
・
・
・
・

私「待って？」
私「これ、 `ア` とか `ハ` で纏まってなくない？」
私「 `辞書みたいなイメージ` が先行してたけどこれ `問題と違う` よね？」

私「（大混乱）」

私「落ち着け、落ち着け私。きっと `ここまでの課程で考えてきたこと` と今までの経験が私を助けてくれるはず。」

私「グループ化するっていう発送は悪くないはず。 `キーがインデックス、valueがそのインデックスに含まれる文字、みたいなハッシュ` を作ったらいいんじゃないか？マッピング的な……ちょっと違う気がするけど……。」

私「あ〜お、って `範囲オブジェクト` でいける？数字とアルファベット以外でもいける？ええい、ままよ！食らえ！」

```ruby

 ('ア'..'オ').to_a
 => ["ア", "ィ", "イ", "ゥ", "ウ", "ェ", "エ", "ォ", "オ"] 
```

私「すっっっっっっっっっっっっご」

私「まじか……いやまあそうよな……できるよな……できるんか……凄いなRuby……。」

私「待てよ、カタカナだったらあれよな、 `ヴ` とかあるよな。え、ヴァネッサとか出てくるかな電話帳。いやでもこれ苗字だけとか書いてないしな。友達にヴァネッサとかおるかもしれんし……ヴァネッサだけ行き場ないとか可哀想よな……。」

ラテ「そうして出来上がった定数がこれ。」

```ruby

INDEX = {
  'ア': ('ア'..'オ').to_a << 'ヴ',
  'カ': ('カ'..'ゴ').to_a,
  'サ': ('サ'..'ゾ').to_a,
  'タ': ('タ'..'ド').to_a,
  'ナ': ('ナ'..'ノ').to_a,
  'ハ': ('ハ'..'ボ').to_a,
  'マ': ('マ'..'モ').to_a,
  'ヤ': ('ヤ'..'ヨ').to_a,
  'ラ': ('ラ'..'ロ').to_a,
  'ワ': ('ワ'..'ン').to_a
}.freeze
```

私「 `ン` っている？？？」
私「いや、ンダホさんとかおるかもしれんし……」

私「入れよう」

私「問題はここからよな」
私「 `ア` は `ア` のグループ、ってグループ分けしたい。」
私「グループ分けしたいけどどうやってメンバー選出する……？」

※シンキングタイム

私「メンバー選出といえば `select` やんけ…………。」
私「さっき作った範囲の中に頭文字が当てはまる子（文字列）を選出してあげたらいいんじゃん？ `include?` でそれはチェックできる……で、それをうまいこと配列にして……なんかやっと見えてきたぞ…………。」

```ruby

class NameIndex
  INDEX = {
    'ア': ('ア'..'オ').to_a << 'ヴ',
    'カ': ('カ'..'ゴ').to_a,
    'サ': ('サ'..'ゾ').to_a,
    'タ': ('タ'..'ド').to_a,
    'ナ': ('ナ'..'ノ').to_a,
    'ハ': ('ハ'..'ボ').to_a,
    'マ': ('マ'..'モ').to_a,
    'ヤ': ('ヤ'..'ヨ').to_a,
    'ラ': ('ラ'..'ロ').to_a,
    'ワ': ('ワ'..'ン').to_a
  }.freeze

  def self.create_index(names)
    INDEX.map do |key, value|
      index_names = names.select { |name| value.include?(name.chr) }
    end
  end
end
```

私「そうそう、これで最初に想定してたみたいに `要素をソート` して。 `index_names` が空じゃなかったらインデックスをつけてあげればいけるんじゃないか。」

```ruby

class NameIndex
  INDEX = {
    'ア': ('ア'..'オ').to_a << 'ヴ',
    'カ': ('カ'..'ゴ').to_a,
    'サ': ('サ'..'ゾ').to_a,
    'タ': ('タ'..'ド').to_a,
    'ナ': ('ナ'..'ノ').to_a,
    'ハ': ('ハ'..'ボ').to_a,
    'マ': ('マ'..'モ').to_a,
    'ヤ': ('ヤ'..'ヨ').to_a,
    'ラ': ('ラ'..'ロ').to_a,
    'ワ': ('ワ'..'ン').to_a
  }.freeze

  def self.create_index(names)
    INDEX.map do |key, value|
      index_names = names.select { |name| value.include?(name.chr) }.sort
      [key.to_s, index_names] unless index_names.empty?
    end
  end
end

names = ['キシモト', 'イトウ', 'ババ', 'カネダ', 'ワダ', 'ハマダ']

NameIndex.create_index(names)

=>[["ア", ["アマネ"]], ["カ", ["キシモト"]], nil, nil, nil, ["ハ", ["ハマダ", "ババ"]], nil, nil, nil, ["ワ", ["ワダ"]]]
```

私「盲点」

私「うっそやん………そりゃそうだわ…………なんで気付かんのじゃ……。」

私「 `compact` しよ………。」
私「ごちゃごちゃしてるし、 `names` が空だったら素直に早期リターンしよ」

ラテ「そして完成したのがこれ。」

```ruby

class NameIndex
  INDEX = {
    'ア': ('ア'..'オ').to_a << 'ヴ',
    'カ': ('カ'..'ゴ').to_a,
    'サ': ('サ'..'ゾ').to_a,
    'タ': ('タ'..'ド').to_a,
    'ナ': ('ナ'..'ノ').to_a,
    'ハ': ('ハ'..'ボ').to_a,
    'マ': ('マ'..'モ').to_a,
    'ヤ': ('ヤ'..'ヨ').to_a,
    'ラ': ('ラ'..'ロ').to_a,
    'ワ': ('ワ'..'ン').to_a
  }.freeze

  def self.create_index(names)
    return [] if names.empty?

    INDEX.map do |key, value|
      index_names = names.select { |name| value.include?(name.chr) }.sort
      [key.to_s, index_names] unless index_names.empty?
    end.compact
  end
end
```

私「あっ、あ……RSpecも書き直しやん…………。先輩ごめんなさい………。（１５分オーバー）」

### フェーズ１で学んだこと

- カタカナも範囲オブジェクトにできる。
- `select` を使えばグループ化は簡単
- 慢心しない、簡単にできたと思った時ほど見直しはしっかり。
- 時間に余裕を持って作業する。
- 問題は定期的に読み直す。自分の認識が間違っていないか確認する。
- 方向性を見失わないようにテストファーストで作業をする。


## フェーズ２　タピオカ先輩からレビューをもらう
この課題を解いた後、見守っていたタピオカ先輩からレビューをもらった。

タピ「レビューしたんだけどさ。」
私「はい。」
タピ「今回悪いところなかった。」
私「？？？」
私「またまたぁ」
タピ「いや本当。今回の評価ポイント（要件を満たせているか・可読性・テストコードの充実度）は満たせてる。」
私「（動揺）（困惑）」
タピ「とりあえずレビューしていこうか」

### ポイント１　injectで無駄なく回す
タピ「私さん、 `map` `compact` してたじゃん。」
私「はい。`map`だと`nil`が混在しちゃうので」
タピ「じゃあ、`nil`が混ざらないようにループ回したらよかったんじゃない？」
私「アッ」

- `map compact` or `select map`で作れる配列は `inject` で作れ
- `inject`で作れるなら `each_with_object`にしなさい（RuboCopの好み）

タピ「[この記事](https://qiita.com/vsanna/items/685018e9e2d75e005c33)が参考になる。」
タピ「今回の問題だと、`nil`になるものののチェックで無駄に処理が走るよね？例えば、電話帳にはア行しか登録されてないのに、他の行についての処理も走る。ループでいうなら２回ループが走ってる。必要なものだけの配列を作りたいなら `inject` を使えば１回で済む。」
私「そっか……そっか……そうですね……（知識としては持っていたのに未だ使いこなせていないことに対する悔しさに襲われる）」

### ポイント２　冗長にならないようなコーディングをする
タピ「あとさ、定数のハッシュ。キーと配列の０番目の値が一緒なの、なんか冗長じゃない？」
私「確かに……。」
タピ「しかもハッシュのキー、`to_s`してるじゃん、`to_s`するくらいなら`ロケット記法`で最初からキーを文字列にしておけばよかったんじゃない？」
私「確かに……。」
タピ「しかもこのキーって配列作るときに使ってるだけじゃん？」
私「そうですね。そのためだけに用意しちゃいました。そのキーでまとめなきゃ！！って気持ちが強くて、キーを。」
タピ「つまりこう定義したら万事解決だったってこと。」

```ruby
class NameIndex
  SYLLABARIES = [
    ('ア'..'オ').to_a << 'ヴ',
    ('カ'..'ゴ').to_a,
    ('サ'..'ゾ').to_a,
    ('タ'..'ド').to_a,
    ('ナ'..'ノ').to_a,
    ('ハ'..'ボ').to_a,
    ('マ'..'モ').to_a,
    ('ヤ'..'ヨ').to_a,
    ('ラ'..'ロ').to_a,
    ('ワ'..'ン').to_a
  ].freeze

  class << self
    def create_index(names)
      names.sort.group_by(&method(:initial)).to_a
    end

    private

    def initial(name)
      SYLLABARIES.find { |values| values.include?(name[0]) }.first
    end
  end
end
```

私「これです」

私「こういうの書きたかったんです」

私「`group_by`使われてるし、メインのメソッドは一行で書かれてるし、超カッケー（配列か〜〜〜〜〜！！マッピングしなきゃって気持ちが強すぎた）」

### 〜タピオカ先輩の解説タイム〜

タピ「例えば、ア〜オ＋ヴの中に、名字の１文字目が含まれていたらア行に纏められるわけでしょ。`group_by`の中でアカサタナ〜が返れば今回の想定した配列が作れるじゃん。つまり、


『名字の1文字目が`SYLLABARIES`の配列のどれかに含まれていたら、その配列の1文字目が返ればいい』

ってことね。それが`initial`メソッド。」

タピ「そのメソッドを`group_by`と合わせて実行すればいいだけ。今回は `&method`を使って書いてみた。」

私「（`group_by`とはこうやって使うのか）」

タピ「どう？納得？」

私「納得しかないです……すげ……」


### フェーズ2で学んだこと
- `group_by`はブロック内の返り値でグルーピングされる＝＞自分の思う返り値が返るようなメソッドを作って呼んであげればグルーピングはできる（今回は`group_by`が使えた！）
- `&method`を使うと短く書ける（参考： [&演算子と、procと、Object#method について理解しなおす](https://qiita.com/kasei-san/items/0392097791d3a5998216)）
- 冗長だと感じるところは徹底的に排除する（同じ文字がなんども出てくる、等。今回は定数の中身が冗長だった。）


## 最後に
やり方、考え方の方向性は合っていましたが、テクニカルな部分がまだまだだなと実感しました。しかしこれで `group_by`はマスターです。`&method`はあまり使ったことがなかったので、使える場面では使っていきたいと思います。
また、自分の書くコードは冗長になりがちなので、「これって冗長じゃない？」という気持ちをもってコーディングすることを意識する必要があるなと感じました。
次の回ではもっといいコードを書けるように腕を磨いておきます。