今回のエンジニアブログを担当する加賀です。
C# 3.5でのLINQの小技を紹介したいと思います。
今回のコードはVisual Studio 2010 Pro SP1、C# 3.5で確認しています。
複数の配列から重複を除いた一覧を取得
class PartyData { public int[] unitIds; // ユニット番号 } List<PartyData> partyList;
以上のようなクラスとメンバーで、partyList内のunitIds配列から、ユニット番号を重複なしで取得します。
LINQを使わない場合は以下のような処理が考えられると思います。
// partyListの初期化 List<PartyData> partyList = new List<PartyData> () { new PartyData(){ unitIds = new[]{3, 5, 7, 10} }, new PartyData(){ unitIds = new[]{5, 12, 13, 15} }, new PartyData(){ unitIds = new[]{7, 13, 15, 20} } }; // 処理 List<int> ids = new List<int> (); foreach (PartyData data in partyList) { foreach (int value in data.unitIds) { if (!ids.Contains (value)) { // まだidsリストにない値のみ追加 ids.Add (value); } } }
LINQのSelectManyとDistinctを使用すれば、より簡潔に記述できます。
SelectManyは、シーケンスの要素に対してシーケンスを返すデリゲートを使用し、最後にそれぞれ返されてきたシーケンスを一つにまとめてくれます。
Distinctは、シーケンスから重複する要素を除いたシーケンスを返してくれます。
// 処理 List<int> ids = partyList .SelectMany (data => data.unitIds) // int[](シーケンス)を返す .Distinct () // 重複排除 .ToList ();
シーケンスから直接ForEachを呼び出す
Listなら直接呼び出せるList
ただ、配列には直接呼び出す形のものは存在せず、System.Arrayクラスのstatic関数、Array.ForEach
さらに、LINQ途中のシーケンスであるIEnumerable
これらを実現するには拡張メソッドとして実装します。
拡張メソッドとは、以下の条件で実装すると、その型のインスタンスメソッドのように呼び出す事が出来ます。
- staticクラス
- public staticメソッド
- 第1引数にて、型の前にthisをつけて、呼び出させたい型を指定
ただし、呼び出させたい型のpublicなメソッド、プロパティ、フィールドのみアクセス可能です。
using System; public static class Utils { // 配列版 // int[] intArray; // intArray.ForEach (~); のように呼び出せる // 本来は Array.ForEach (intArray, ~); public static void ForEach<T> (this T[] source, Action<T> action) { Array.ForEach (source, action); } // IEnumerable<T>版 // Enumerable.Repeat (0, 5).ForEach (~); のように呼び出せる // 本来は Enumerable.Repeat (0, 5).ToList ().ForEach (~); か // Array.ForEach (Enumerable.Repeat (0, 5).ToArray (), ~); public static void ForEach<T> (this IEnumerable<T> source, Action<T> action) { foreach (T val in source) { action (val); } } }
まとめ
LINQをうまく活用すると、ループさせる部分を記述しなくても、シーケンスの要素それぞれの処理を行えます。
ForEachは、配列やIEnumerable
LINQをうまく活用して、ループ処理や汎用処理をわざわざ書かなくてもいいようにしていきたいです。