地平線に行く

2011-10-31

LINQの拡張メソッド一覧と、ほぼ全部のサンプルを作ってみました。

| 22:40 |

C#LINQメソッドは超便利!!、なんですが…肝心のAPIがわかりづらいです。

そこで、種類ごとにまとめて、簡単なサンプルを書いてみました。



要素の取得(単一)

メソッド機能
ElementAt
ElementAtOrDefault
指定した位置(インデックス)にある要素を返します。
First
FirstOrDefault
最初の要素を返します。
Last
LastOrDefault
最後の要素を返します。
Single
SingleOrDefault
唯一の要素を返します。該当する要素が複数ある場合、例外をスローします。

該当の要素がない場合は…

 「〜OrDefault」が付いていないメソッドは例外をスローします。

 「〜OrDefault」が付いたメソッドは型の規定値を返します。

var source = new[] { 11, 16, 52, 21, 8, 8 };

Console.WriteLine(source.ElementAt(2));
// -> 52
Console.WriteLine(source.ElementAtOrDefault(8));
// -> 0
Console.WriteLine(source.First());
// -> 11
Console.WriteLine(source.First(e => e > 15));
// -> 16
Console.WriteLine(source.Last());
// -> 8
Console.WriteLine(source.Last(e => e > 15));
// -> 21
//Console.WriteLine(source.Single());
// -> System.InvalidOperationException: シーケンスに複数の要素が含まれています
Console.WriteLine(source.Single(e => e > 50));
// -> 52

要素の取得(複数)

メソッド機能
Where条件を満たす要素をすべて返します。
Distinct重複を除いたシーケンスを返します。
Skip先頭から指定された数の要素をスキップし、残りのシーケンスを返します。
SkipWhile先頭から指定された条件を満たさなくなるまで要素をスキップし、残りのシーケンスを返します。
Take先頭から指定された数の要素を返します。
TakeWhile先頭から指定された条件を満たす要素を返します。
var source = new[] { 11, 16, 52, 21, 8, 8 };

Console.WriteLine(source.Where(e => e > 20).ToResult());
// -> {52, 21}
Console.WriteLine(source.Distinct().ToResult());
// -> {11, 16, 52, 21, 8}
Console.WriteLine(source.Skip(3).ToResult());
// -> {21, 8, 8}
Console.WriteLine(source.SkipWhile(e => e < 20).ToResult());
// -> {52, 21, 8, 8}
Console.WriteLine(source.Take(3).ToResult());
// -> {11, 16, 52}
Console.WriteLine(source.TakeWhile(e => e < 20).ToResult());
// -> {11, 16}

集計

メソッド機能
Max最大値を返します。
Min最小値を返します。
Average平均値を返します。
Sum合計を返します。
Count素数を返します。
Aggregateアキュムレータ関数で処理した結果を返します。
var source = new[] { 11, 16, 52, 21, 8, 8 };

Console.WriteLine(source.Max());
// -> 52
Console.WriteLine(source.Min());
// -> 8
Console.WriteLine(source.Average());
// -> 19.3333333333333
Console.WriteLine(source.Sum());
// -> 116
Console.WriteLine(source.Count());
// -> 6
Console.WriteLine(source.Aggregate((now, next) => Math.Max(now, next)));
// -> 52

// 参考:標本分散
double ave = source.Average();
Console.WriteLine(source.Sum(e => Math.Pow(e - ave, 2)) / source.Count());
// -> 234.555555555556

判定

メソッド機能
Allすべての要素が条件を満たしてるか判定します。
Any条件を満たす要素が含まれているか判定します。
Contains指定した要素が含まれているかどうかを判定します。
SequenceEqual2つのシーケンスが等しいかどうかを判定します。
var source = new[] { 11, 16, 52, 21, 8, 8 };

Console.WriteLine(source.All(e => e > 10));
// -> False
Console.WriteLine(source.Any(e => e > 10));
// -> True
Console.WriteLine(source.Contains(20));
// -> False
Console.WriteLine(source.SequenceEqual(new[] { 11, 16, 21, 52, 8, 8 }));
// -> False

集合

メソッド機能
Union指定したシーケンスとの和集合を返します。
Except指定したシーケンスとの差集合を返します。
Intersect指定したシーケンスとの積集合を返します。
var first = new[] { 11, 16, 52, 21, 8, 8 };
var second = new[] { 16, 21, 20, 3 };

Console.WriteLine(first.Union(second).ToResult());
// -> {11, 16, 52, 21, 8, 20, 3}
Console.WriteLine(first.Except(second).ToResult());
// -> {11, 52, 8}
Console.WriteLine(first.Intersect(second).ToResult());
// -> {16, 21}

ソート

メソッド機能
OrderBy昇順にソートしたシーケンスを返します。
OrderByDescending降順にソートしたシーケンスを返します。
ThenByソートしたシーケンスに対し、キーが等しい要素同士を昇順にソートしたシーケンスを返します。
ThenByDescendingソートしたシーケンスに対し、キーが等しい要素同士を降順にソートしたシーケンスを返します。
Reverse逆順にソートしたシーケンスを返します。

var source = new[] {
    new{Name = "C#", Age = 11},
    new{Name = "Java", Age = 16},
    new{Name = "Groovy", Age = 8},
    new{Name = "Scala", Age = 8},
};

Console.WriteLine(source.OrderBy(e => e.Age).ToResult());
// -> {{ Name = Groovy, Age = 8 },
//     { Name = Scala, Age = 8 }, 
//     { Name = C#, Age = 11 },
//     { Name = Java, Age = 16 }}
Console.WriteLine(source.OrderByDescending(e => e.Age).ToResult());
// -> {{ Name = Java, Age = 16 },
//     { Name = C#, Age = 11 },
//     { Name = Groovy, Age = 8 },
//     { Name = Scala, Age = 8 }}
Console.WriteLine(source.OrderBy(e => e.Age)
                        .ThenBy(e => e.Name.Length).ToResult());
// -> {{ Name = Scala, Age = 8 },
//     { Name = Groovy, Age = 8 },
//     { Name = C#, Age = 11 },
//     { Name = Java, Age = 16 }}
Console.WriteLine(source.OrderBy(e => e.Age)
                        .ThenByDescending(e => e.Name.Length).ToResult());
// -> {{ Name = Groovy, Age = 8 },
//     { Name = Scala, Age = 8 },
//     { Name = C#, Age = 11 },
//     { Name = Java, Age = 16 }}
Console.WriteLine(source.Reverse().ToResult());
// -> {{ Name = Scala, Age = 8 },
//     { Name = Groovy, Age = 8 },
//     { Name = Java, Age = 16},
//     { Name = C#, Age = 11 }}

射影

メソッド機能
Select1つの要素を単一の要素に射影します。
SelectMany1つの要素から複数の要素に射影します。その結果を1つのシーケンスとして返します。
GroupBy指定のキーで要素をグループ化します。その "キーとグループ" のシーケンスを返します。
var source = new[] {
    new{Name = "C#", Age = 11},
    new{Name = "Java", Age = 16},
    new{Name = "Groovy", Age = 8},
    new{Name = "Scala", Age = 8},
};

Console.WriteLine(source.Select(e => e.Name).ToResult());
// -> {C#, Java, Groovy, Scala}
Console.WriteLine(source.SelectMany(e => e.Name.ToCharArray()).ToResult());
// -> {C, #, J, a, v, a, G, r, o, o, v, y, S, c, a, l, a}
Console.WriteLine(source.GroupBy(e => e.Age).ToResult());
// -> {Key=11, Source={{ Name = C#, Age = 11 }},
//     Key=16, Source={{ Name = Java, Age = 16 }},
//     Key=8, Source={{ Name = Groovy, Age = 8 }, { Name = Scala, Age = 8 }}}

結合

メソッド機能
Join内部結合を行ったシーケンスを返します。
GroupJoin左外部結合を行って指定のキーでグループ化します。その "キーとグループ" のシーケンスを返します。
Concat2つのシーケンスを連結します。
(Unionは同じ要素を一つにまとめますが、Concatは元の要素をすべて返します。)
DefaultIfEmptyシーケンスを返します。シーケンスが空なら、規定値もしくは任意の要素を返します。
Zip指定した関数で、2つのシーケンスを1つのシーケンスにマージします。
var outer = new[] {
    new{Name = "C#", Age = 11},
    new{Name = "Java", Age = 16},
    new{Name = "Groovy", Age = 8},
    new{Name = "Scala", Age = 8},
};
var outer2 = new[] {
     new{Name = "Python", Age = 21},
     new{Name = "COBOL", Age = 52},
};
var inner = new[] {
    new{Name = "C#", DesignedBy = "Microsoft"},
    new{Name = "Java", DesignedBy = "Sun Microsystems"},
    new{Name = "Java", DesignedBy = "Oracle"},
};

Console.WriteLine(outer.Join(inner,
                             o => o.Name,
                             i => i.Name, 
                             (o, i) => new { o.Name, o.Age, i.DesignedBy}).ToResult());
// -> {{ Name = C#, Age = 11, DesignedBy = Microsoft },
//     { Name = Java, Age = 16, DesignedBy = Sun Microsystems },
//     { Name = Java, Age = 16, DesignedBy = Oracle }}
Console.WriteLine(outer.GroupJoin(inner,
                                  o => o.Name,
                                  i => i.Name,
                                  (o, i) => new { o.Name, o.Age, DesigndBy = i.Select(e => e.DesignedBy).ToResult() }).ToResult());
// -> {{ Name = C#, Age = 11, DesigndBy = {Microsoft} },
//     { Name = Java, Age = 16, DesigndBy = {Sun Microsystems, Oracle} },
//     { Name = Groovy, Age = 8, DesigndBy = {} },
//     { Name = Scala, Age = 8, DesigndBy = {} }}
Console.WriteLine(outer.Concat(outer2).ToResult());
// -> {{ Name = C#, Age = 11 },
//     { Name = Java, Age = 16 },
//     { Name = Groovy, Age = 8 },
//     { Name = Scala, Age = 8 },
//     { Name = Python, Age = 21 },
//     { Name = COBOL, Age = 52 }}
Console.WriteLine(outer.DefaultIfEmpty().ToResult());
// -> {{ Name = C#, Age = 11 },
//     { Name = Java, Age = 16 },
//     { Name = Groovy, Age = 8 },
//     { Name = Scala, Age = 8 }}
Console.WriteLine(outer.Zip(outer2, (o1, o2) => o1.Name + "&" + o2.Name).ToResult());
// -> {C#&Python, Java&COBOL}

変換

メソッド機能
OfType各要素を指定した型に変換します。
キャストできない要素は除外します。
Cast各要素を指定した型に変換します。
キャストできない要素が含まれていた場合、例外をスローします。
ToArray配列を作成します。
ToDictionary連想配列(ディクショナリ)を作成します。
ToListリストを生成します。
ToLookupキーコレクション*1を生成します。
AsEnumerableIEnumerable<T> を返します。*2
ArrayList mixed = new ArrayList { "C#", "Java", 3.141592653, "Groovy", "Scala" };

Console.WriteLine(mixed.OfType<string>().ToResult());
// -> {C#, Java, Groovy, Scala}

//Console.WriteLine(mixed.Cast<string>().ToResult());
// -> System.InvalidCastException: 
//    型 'System.Double' のオブジェクトを型 'System.String' にキャストできません。

補足 - 結果表示用の拡張メソッド

static String ToResult<TSource>(this IEnumerable<TSource> source)
{
    return "{" + string.Join(", ", source) + "}";
}

static String ToResult<TKey, TSource>(this IEnumerable<IGrouping<TKey, TSource>> source)
{
    return source.Select(group => string.Format("Key={0}, Source={1}", group.Key, group.ToResult())).ToResult();
}

*1:1対多のディクショナリ。例えば、〜.ToLookup()["hoge"] と実行すると、"hoge" に紐付く要素の集合(IEnumerable)が返ってきます。

*2:IEnumerable と同じ名前のメソッドがクラス内に定義されている場合に使います。そのままだと、クラス内のメソッドが優先的に選択されて、IEnumerable の拡張メソッドが呼びだせないためです。

トラックバック - http://d.hatena.ne.jp/chiheisen/20111031/1320068429
リンク元