LINQ to Object のイディオム その2 : 集計演算子, 要素演算子, 限定子

ちょっといない隙に PLINQ/TPLVoltaInternational Pack とかw VS2008 が出たばっかりなのに、なんなんだろう(^^; 遠い未来の夢と思ってたライブラリが急に目の前にポンと出てきて、なんだか戸惑ってしまってます。で、どれで遊ぶか迷った末に決めかねて結局 LINQ to Object で地味に(?)遊んでみました。

LINQ to Object の変なコードばっかり載せてしまってるので、たまには実用的な例を一挙に。遅くなるだけ、ややこしいだけじゃない LINQ to Object の簡単で便利なコードサンプルです。このサンプルのあたりなら、誰もが今すぐに恩恵を受けられるハズ。

using System;
using System.Collections.Generic;
using System.Linq;

static class Program
{
  static void Main()
  {
    var dic = new Dictionary<String, int>
      { { "バナナ", 10 }, { "いちご", 398 }, { "りんご", 298 } };

    Console.WriteLine( "Keys: " + String.Join( ", ", dic.Keys.ToArray() ) );
    Console.WriteLine( "Values: " + String.Join( ", ",
      dic.Values.Select( n => n.ToString() ).ToArray() ) );

    Console.WriteLine( "Reverse: " +
      String.Join( ", ",
        ( from n in dic.Values.Reverse() select n.ToString() ).ToArray() ) );
    Console.WriteLine( "Sum: " + dic.Values.Sum() );
    Console.WriteLine( "Sum2: " + dic.Sum( e => e.Value ) );
    Console.WriteLine( "Count: " + dic.Count() );
    Console.WriteLine( "Count (n < 300): " + dic.Count( e => e.Value < 300 ) );
    Console.WriteLine( "Min: " + dic.Min( e => e.Value ) );
    Console.WriteLine( "Max: " + dic.Values.Max() );
    Console.WriteLine( "Average: " + dic.Average( e => e.Value ) );
    Console.WriteLine( "Reverse (Aggregate): " +
      dic.Keys.Reverse().Aggregate( "", ( s, n ) => s + n.ToString() + ", " ) );

    Console.WriteLine( "dic[ 2 ]: " + dic.ElementAt( 2 ) );
    Console.WriteLine( "Contains いちご: " + dic.Keys.Contains( "いちご" ) );
    Console.WriteLine( "Contains 柿: " + dic.Keys.Contains( "柿" ) );
    Console.WriteLine( "All (10 <= n): " + dic.Values.All( n => 10 <= n ) );
    Console.WriteLine( "All (n < 100): " + dic.Values.All( n => n < 100 ) );
    Console.WriteLine( "Any (n < 5): " + dic.Any( e => e.Value < 5 ) );
    Console.WriteLine( "Any (n < 100): " + dic.Values.Any( n => n < 100 ) );

    Console.WriteLine( "SingleOrDefault (n < 100): " +
      dic.SingleOrDefault( e => e.Value < 100 ) );
    Console.WriteLine( "SingleOrDefault (n == 5): " +
      dic.SingleOrDefault( e => e.Value == 5 ) );
    Console.WriteLine( "First: " + dic.First() );
    Console.WriteLine( "Last: " + dic.Last() );

    Console.ReadKey();
  }
}

実行結果

Keys: バナナ, いちご, りんご
Values: 10, 398, 298
Reverse: 298, 398, 10
Sum: 706
Sum2: 706
Count: 3
Count (n < 300): 2
Min: 10
Max: 398
Average: 235.333333333333
Reverse (Aggregate): りんご, いちご, バナナ,
dic[ 2 ]: [りんご, 298]
Contains いちご: True
Contains 柿: False
All (10 <= n): True
All (n < 100): False
Any (n < 5): False
Any (n < 100): True
SingleOrDefault (n < 100): [バナナ, 10]
SingleOrDefault (n == 5): [, 0]
First: [バナナ, 10]
Last: [りんご, 298]

いろんな書き方ができることを示すために、わざといろいろな書き方をしました。C#2.0 では、Array と List だけは便利なメソッドがたくさんあるけど、Dictionary あたりになると全部自分で書かなければいけなかったのが、LINQ to Object で一気に解決してしまいました。ありがたいことです。LINQ の勉強をどこから始めようか迷ってるなら、このあたりが最も実用的と思うのでおすすめです。

参考: .NET 標準クエリ演算子

(追記)辞書のエントリ順に依存したコードは書かないようにご注意を。あくまでもサンプルですので。念のため。