LINQで頻度を求める

昨日の関連の話題。コード成分が足りなかったので補給に。
Dictionary を使って頻度を求めるのは定番中の定番の使い方だと思います。たとえば、"the quick brown fox jumps over the lazy dog" の文字列の中で、a が何回使われてて、b が何回使われているかっていう数え上げ処理のこと。頻繁に使うってほどでもないけど、これまで、何回も何回も何回も書きましたw
昔、Perl をよく使ったのはこれがやりたかったからでした。Perl に慣れてからは正規表現のほうが Perl の重要な機能になったけど、それまではハッシュが使いたくて Perl してました。まだ C# もないころで、ハッシュをスマートに使える言語をほかに知らなかったから。
これを LINQ にしてみた。
まずは LINQ なしで。多少違いはあってもだいたいこんなコードになるはず。

static Dictionary<T, int> ToFrequency<T>( this IEnumerable<T> source )
{
    var dic = new Dictionary<T, int>();

    foreach ( var item in source )
    {
        if ( dic.ContainsKey( item ) )
        {
            dic[ item ]++;
        }
        else
        {
            dic[ item ] = 1;
        }
    }
    return dic;
}

一度 ContainsKey で調べる処理が入るのが無駄っぽくてイヤだ。キーがない場合の初期値を式でわたせればいいのに。
LINQ 版。

source.GroupBy( x => x ).ToDictionary( g => g.Key, g => g.Count() );

おっ?こんなに短く書けるとは。でもわかりづらいかも…?
比べてみる。

static void Main()
{
    var str = "the quick brown fox jumps over the lazy dog";

    var dic = str.ToFrequency();

    var dic2 = str.GroupBy( c => c )
        .ToDictionary( g => g.Key, g => g.Count() );

    Console.WriteLine(
      dic.OrderBy( x => x.Key )
      .SequenceEqual( dic2.OrderBy( x => x.Key ) ) );

    foreach ( var item in dic2 )
    {
                Console.WriteLine( item.Key + " : " +  item.Value );
    }
    Console.ReadKey();
}

同じ!