levenshtein gem で DroidKaigi と DanKogai の類似度を測定する #ruby #droidkaigi

## <i class="fa fa-android" style="font-size:1em;"></i> 概要
**levenshtein gem** で **<i class="fa fa-android" style="font-size:1em;"></i> [DroidKaigi](http://droidkaigi.github.io/)** と **:sunglasses::white_circle: DanKogai** の **類似度** を測定します

## <i class="fa fa-history" style="font-size:1em;"></i> 経緯
このあたりのツイートがことのはじめ。

<blockquote class="twitter-tweet" lang="ja"><p>「DanKogai やります」かと思ったら DroidKaigi の空見だった件</p>&mdash; mattn (@mattn_jp) <a href="https://twitter.com/mattn_jp/status/562448976171909120">2015, 2月 3</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

自分も同時に、 **「あー、 DanKogai に見えるな」** と思っていたので
DroidKaigi のツイートに参戦していた。

~~~
(ฅ・ω・)ฅ ／{  ドロイド君にサスペンダーをつける職人さんまだー } DroidKaigi DanKogai
~~~

と呟いたら・・・

<blockquote class="twitter-tweet" lang="ja"><p><a href="https://twitter.com/tbpgr">@tbpgr</a> 遅くなってすみません。ご査収ください。 <a href="http://t.co/ag3Vve9rCP">pic.twitter.com/ag3Vve9rCP</a></p>&mdash; ハト (@rosylilly) <a href="https://twitter.com/rosylilly/status/562461512506150912">2015, 2月 3</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

作っていただけた！
**@rosylilly さん、ありがとうございます！** :book:

ちなみに **DanKogai さんご本人の目にも止まり、こんな結果になった模様**。

<blockquote class="twitter-tweet" lang="ja"><p>アイコンこれにしたろか… &lt;<a href="https://twitter.com/rosylilly">@rosylilly</a>: DanKogai 、すごく楽しみなイベントになった <a href="http://t.co/aW1f4ZyzeC">pic.twitter.com/aW1f4ZyzeC</a></p>&mdash; Dan Kogai (@dankogai) <a href="https://twitter.com/dankogai/status/562476507897942016">2015, 2月 3</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

## <i class="fa fa-android" style="font-size:1em;"></i> 本題
levenshtein gem で **`DroidKaigi`** と **`DanKogai`** の類似度を測定します。
levenshtein は <i class="fa fa-external-link" style="font-size:1em;"></i>**[レーベンシュタイン距離](http://ja.wikipedia.org/wiki/%E3%83%AC%E3%83%BC%E3%83%99%E3%83%B3%E3%82%B7%E3%83%A5%E3%82%BF%E3%82%A4%E3%83%B3%E8%B7%9D%E9%9B%A2)** で類似度を計測します。

### <i class="fa fa-flask" style="font-size:1em;"></i> サンプルコード[^1]

~~~ruby
require 'levenshtein'
require 'pp'

module Levenshtein
  module_function
  def call(main_word)
    levenshtein =->(x, y) { Levenshtein.similarity(x, y) }
    levenshtein.curry[main_word]
  end

  def similarity(one, other)
    1 - Levenshtein.normalized_distance(one, other)
  end
end

class Array
  def sort_by_similarity_desc_word_asc
    sort_by { |e|[-e[:similarity], e[:word]] }
  end
end

module GestaltKogai
  module_function
  MAIN_WORD = 'DanKogai'
  GESTALT_WORDS = %w(
    DanKogai
    DroidKaigi
    DenKigai
    DonKonishi
    DensyaotoKo
    DonkeyKong
    DanNorth
    DoxyGen
    DoorKeeper
    DaisuKi
    DaiKirai
    DaiKanYama
    DanjyoKosai
    DandanKokorohikareteku
    DondoKodon
    DamKaraoke
    Horiemon
  )

  def similarities
    kogai_levenshtein = Levenshtein.(MAIN_WORD)
    GESTALT_WORDS.each_with_object([]) do |gestalt_word, memo|
      memo << {
        word: gestalt_word,
        similarity: kogai_levenshtein.(gestalt_word)
      }
    end
  end
end

results = GestaltKogai.similarities
pp results.sort_by_similarity_desc_word_asc
~~~

### <i class="fa fa-file-o" style="font-size:1em;"></i> 出力

~~~ruby
[{:word=>"DanKogai", :similarity=>1.0},
 {:word=>"DenKigai", :similarity=>0.75},
 {:word=>"DanjyoKosai", :similarity=>0.6363636363636364},
 {:word=>"DaiKirai", :similarity=>0.625},
 {:word=>"DanNorth", :similarity=>0.5},
 {:word=>"DonKonishi", :similarity=>0.5},
 {:word=>"DaiKanYama", :similarity=>0.4},
 {:word=>"DamKaraoke", :similarity=>0.4},
 {:word=>"DondoKodon", :similarity=>0.4},
 {:word=>"DaisuKi", :similarity=>0.375},
 {:word=>"DonkeyKong", :similarity=>0.30000000000000004},
 {:word=>"DroidKaigi", :similarity=>0.30000000000000004},
 {:word=>"DandanKokorohikareteku", :similarity=>0.2727272727272727},
 {:word=>"DensyaotoKo", :similarity=>0.2727272727272727},
 {:word=>"DoorKeeper", :similarity=>0.19999999999999996},
 {:word=>"DoxyGen", :similarity=>0.125},
 {:word=>"Horiemon", :similarity=>0.0}]
~~~

## <i class="fa fa-android" style="font-size:1em;"></i> おまけ
did_you_mean という gem というタイポ検出ツールがあって、
その中でもlevenshtein は <i class="fa fa-external-link" style="font-size:1em;"></i> [レーベンシュタイン距離](http://ja.wikipedia.org/wiki/%E3%83%AC%E3%83%BC%E3%83%99%E3%83%B3%E3%82%B7%E3%83%A5%E3%82%BF%E3%82%A4%E3%83%B3%E8%B7%9D%E9%9B%A2) で文字列のを用いて、類似度を測っています。

詳しくは下記エントリにまとめてあります。
<i class="fa fa-external-link-square" style="font-size:1em;"></i> [Ruby | did_you_mean gem でスペルミスを検出＋ did_you_mean gem のコードリーディング](http://qiita.com/tbpgr/items/835b3ac168304cba9b7b)

## :bow: お礼の広告コーナー
私の何気ない呟きに反応してドロイド君（ DanKogai ver )を作ってくださった @rosylilly さんへの感謝の気持ちとして
@rosylilly さんの Qiita のエントリを 3 つほど紹介したいと思います。

### :dog2: [本気で使う Docker](http://qiita.com/rosylilly/items/08f4c0771e2ca92a72aa)
今を時めく、 :whale: Docker のエントリです。
はてなブックマーク内でも

`知らなかった -> "quay.io"`
`よかった。本気で使ってる人いたんだ。`
`ふむふむ(´・ω・｀)`
`めっちゃ良い`

など、反響を呼んでいるエントリです。

### :monkey: [Ruby で作る、簡単 CLI ツールのススメ](http://qiita.com/rosylilly/items/d7f9667f3998cfc420d1)
Bundler + Thor を作った gem の作成から、
Private な環境で gem を配布する方法など
**社内で使う小さな便利 gem **に関わるノウハウが分かりやすくまとめてあります。

### :bird: [複数人での Git 開発に便利な 3 つのコマンド](http://qiita.com/rosylilly/items/9648ad2c8aa53465372b)
説明すると丸ごとネタバレになりそうなのでリンク先でお楽しみください。

## :bow: 謝辞
最後になりましたが、 Dan Kogai さんに感謝いたします。
当記事を読んで、もし気を悪くされましたら削除させていただきます。

## :link: 外部資料
* <i class="fa fa-external-link-square" style="font-size:1em;"></i> [Ruby | did_you_mean gem でスペルミスを検出＋ did_you_mean gem のコードリーディング](http://qiita.com/tbpgr/items/835b3ac168304cba9b7b)
* <i class="fa fa-external-link" style="font-size:1em;"></i> [levenshtein gem RubyGems](https://rubygems.org/gems/levenshtein)
* <i class="fa fa-external-link" style="font-size:1em;"></i> [レーベンシュタイン距離](http://ja.wikipedia.org/wiki/%E3%83%AC%E3%83%BC%E3%83%99%E3%83%B3%E3%82%B7%E3%83%A5%E3%82%BF%E3%82%A4%E3%83%B3%E8%B7%9D%E9%9B%A2)

## :books: 脚注
[^1]: Ruby に不慣れな方は知らなそうなものをいっぱい使ってますが、このエントリの主題から外れるので特に解説はしません。気になったら次のキーワードでググってみてください `[ruby curry], [ruby to_proc call], [module module_function], [ruby 配列 %記法]`