0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

電話帳作成問題を解いてみた

Last updated at Posted at 2022-02-07

はじめに

Ruby初心者向けのプログラミング問題を集めてみたから、今回は電話帳作成問題を解いてみました。
自分が書いたソースコードと、先輩にいただいた回答例を紹介していきます。

以下のINのような名前の配列を渡すと、OUTのように50音の音別にグループ分けされた配列が返るようにします。

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

要求仕様

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

自分が書いたソースコード

class NameIndex
  KANA_GROUPS = [
    ('ア'..'オ').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)
    KANA_GROUPS.map do |kana_group|
      target_names = names.sort.select do |name|
        kana_group.include?(name[0])
      end
      [kana_group.first, target_names] unless target_names.empty?
    end.compact
  end
end

処理について

まず、定数KANA_GROUPSで、50音を行ごとに分けた配列を定義しています。

create_indexメソッドでは、KANA_GROUPSをレシーバにmapを使用し、
ループの中では、引数のnamessortで並び替えた後、selectで特定の名前を取得しています。
kana_groupの先頭の文字と、取得した名前たち(target_names)をセットにした配列を作成しています。

また、結果にnilが含まれてしまうので、最後にcompactしています。

改善点

  • KANA_GROUPSに、が含まれていない。
  • mapの中で、namesに対してsortしているので無駄に処理が走ってしまう。
    • ループより前で、sortしたものを変数に入れておくと良かった。
  • 最後にcompactしているので、無駄に処理が走ってしまう。
    • map+compactで書けるときは、injectを使うようにする。
    • injectが使えるときは、each_with_objectを使うようにする。
  • name[0]kana_group.first、記法が統一されていない。
  • selectの部分は、別メソッドに切り出したほうが可読性が上がる。
    • どのような処理をしているのかがメソッド名から想像できるため、分かりやすくなる。

回答例

class NameIndex
  SYLLABLES = [
    [*('ア'..'オ'), 'ヴ'],
    ('カ'..'ゴ').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_syllable)).to_a
    end

    private

    def initial_syllable(name)
      SYLLABLES.find { |column| column.include?(name.chr) }.first
    end
  end
end

処理について

まず、定数SYLLABLESで、50音を行ごとに分けた配列が定義されています。
ア行は、配列展開が使用されており、も含まれています。

create_indexでは、引数のnamessortした後、group_byでグループ分けしたハッシュを配列にしています。
(group_byは、ブロックの返り値をキー、キーに対応する要素の配列を値とするハッシュを返します。)

&method(:initial_syllable)で、要素1つずつに対して、initial_syllableを呼んでいます。

initial_syllableメソッドでは、引数のnameが属するグループの段を返すために、SYLLABLESからnameの1文字目に当てはまる行の先頭を取得しています。

学んだこと

  • 範囲オブジェクトを使うときは、必要な値が全て含まれているか確認する。
  • グルーピングしたいときは、group_byが使えないか検討する。
  • ループを回すときは、条件式が少なく書ける方をレシーバにする。
  • 1つのメソッドで複数の処理をしすぎないようにする。(別メソッドに切り分ける。)

まとめ

今回の電話帳問題を解いてみて、コードの統一感や、1行ずつのコードの役割を意識できていないように感じました。
今後はリファクタリングする時間を多く取って、より良い書き方ができないか、同じような処理はきちんと統一されているか、というところを意識して実装しようと思います。

0
0
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?