久保清隆のブログ

ライフハック、健康、旅行など、役立つ情報を書きます。

Rubyで氏名を五十音順(辞書順)に並び替える

MySQLのorderだと、五十音順がうまく並び替えができなかった。
例えば、辞書順だと、「わだ」、「わたなべ」の順になるはずが、
MySQLだと、「わたなべ」、「わだ」の順になる。
つまり、濁点が後ろに来てしまう。

そういうわけなので、Rubyを使って強引に並び替えることにした。

def sort_by_name(users=[])
  origin = "a-zA-Zァ-ン0-9ぁぃぅぇぉがぎぐげござじずぜぞだぢづでどばびぶべぼぱぴぷぺぽゃゅょっゎァィゥェォガギグゲゴザジズゼゾダヂヅデドバビブベボパピプペポャュョッヮ"
  normalize = "a-zA-Zぁ-ん0-9あいうえおかきくけこさしすせそたちつてとはひふへほはひふへほやゆよつわあいうえおかきくけこさしすせそたちつてとはひふへほはひふへほやゆよつわ"

  users.sort!{|a,b|
    [a.family_name_kana.tr(origin, normalize), a.first_name_kana.tr(origin, normalize)] <=> 
    [b.family_name_kana.tr(origin, normalize), b.first_name_kana.tr(origin, normalize)]
  }
end

users = User.find(:all)
sort_by_name(users)
  • ポイント
    • trを使って全角英数字を半角に変え、濁点・半濁点のある文字は濁点・半濁点をなくし、それをsortで並び替える。
    • カタカナとひらがなが混ざっている場合があるので、カタカナはひらがなに変換してからsortする。
    • 氏名は、苗字と名前を別のカラムにいれているので、sortの中で配列を使って苗字、名前の順で五十音順にsortする。
  • 追記
  users.sort!{|a,b|
    [a.family_name_kana.tr(origin, normalize), a.first_name_kana.tr(origin, normalize)] <=> 
    [b.family_name_kana.tr(origin, normalize), b.first_name_kana.tr(origin, normalize)]
  }

よりも

  users.sort_by{|a| [a.family_name_kana.tr(origin, normalize), a.first_name_kana.tr(origin, normalize)]}

の方がムダがなくて良かった。

  • trについても一応説明

tr(search, replace)、tr!(search, replace)
文字列の中に search 文字列に含まれる文字が存在したら、それを replace 文字列の対応する文字に置き換える。
search の形式は tr(1) と同じ。つまり、 `a-c' は a から c を意味し、"^0-9" のように文字列の先頭が `^' の場合は指定文字以外が置換の対象になる。
replace に対しても `-' による範囲指定が可能。
`-' は文字列の両端にない場合にだけ範囲指定の意味になる。同様に、`^' もその効果は文字列の先頭にあるときだけ。
また、 `-', `^', `\' はバックスラッシュ(`\')によりエスケープすることができる。
replace の範囲が search の範囲よりも小さい場合は、 replace の最後の文字が無限に続くものとして扱われる。
tr は置換後の文字列を生成して返す。 tr! は self を変更して返すが、置換が起こらなかった場合は nil を返す。

プログラミング言語 Ruby リファレンスマニュアル参照。


  • 余談

因みに、最初、

(family_name_kana+first_name_kana).tr(origin,normalize)

でsortをした方がシンプルだなぁと思ってそうしていたら、「わたなべ ほげほげ」さんと「わだ ふがふが」さんの順番で、わださんが後になってしまっていた。苗字と名前をつなげちゃだめだった。


たのしいRuby 第5版

たのしいRuby 第5版


お読み頂きありがとうございます。
少しでもお役に立てたらクリックお願いします↓。

にほんブログ村 IT技術ブログ プログラム・プログラマへ人気ブログランキングへ Subscribe with livedoor Reader


◆◆このブログのサイトマップへ◆◆