LINQで頻度を求める
昨日の関連の話題。コード成分が足りなかったので補給に。
Dictionary
昔、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(); }
同じ!