コレクションのフィルタリング処理
C#で配列やList<T>などのコレクションから、特定の条件を満たす要素だけを取り出したい場合、LINQ(Language Integrated Query)のWhereメソッドを使用するのが最も標準的な方法です。
foreachループとif文を組み合わせて抽出することも可能ですが、Whereメソッドを使用することで、フィルタリングのロジックを宣言的かつ簡潔に記述できます。
この記事では、Whereメソッドの基本的な使い方と、要素の「インデックス(順番)」を条件に加える応用的な使い方について解説します。
基本的な条件による抽出
Whereメソッドは、引数として「条件式(述語)」を受け取ります。コレクションの各要素に対してこの条件式が評価され、結果がtrueとなる要素のみを含む新しいシーケンス(IEnumerable<T>)が返されます。
コード例:在庫のある商品の抽出
商品リストから、在庫数が1以上で、かつ価格が一定以下の商品を抽出する例です。
using System;
using System.Collections.Generic;
using System.Linq; // LINQを使用するために必須
public class Product
{
public string Name { get; set; }
public int Price { get; set; }
public int Stock { get; set; }
}
public class WhereBasicExample
{
public static void Main()
{
// 商品データのリスト
var products = new List<Product>
{
new Product { Name = "ゲーミングマウス", Price = 8000, Stock = 15 },
new Product { Name = "メカニカルキーボード", Price = 15000, Stock = 0 }, // 在庫切れ
new Product { Name = "USBハブ", Price = 2000, Stock = 30 },
new Product { Name = "モニターアーム", Price = 5000, Stock = 5 },
new Product { Name = "Webカメラ", Price = 4500, Stock = 0 } // 在庫切れ
};
// 条件: 在庫 (Stock) が 1 以上、かつ 価格 (Price) が 10000 未満
// Where メソッドにラムダ式で条件を記述する
var availableProducts = products.Where(p => p.Stock > 0 && p.Price < 10000);
Console.WriteLine("--- 条件に一致する商品 ---");
foreach (var item in availableProducts)
{
Console.WriteLine($"商品名: {item.Name}, 価格: {item.Price}円");
}
}
}
出力結果:
--- 条件に一致する商品 ---
商品名: ゲーミングマウス, 価格: 8000円
商品名: USBハブ, 価格: 2000円
商品名: モニターアーム, 価格: 5000円
インデックス(要素番号)を利用した抽出
Whereメソッドには、要素そのものだけでなく、その要素の**インデックス(何番目の要素か)**を使用できるオーバーロード(別バージョンのメソッド)が存在します。
構文: collection.Where((item, index) => 条件式)
item: 現在の要素index: 現在の要素のインデックス(0から始まります)
これを利用すると、「偶数番目の要素だけを取得する」や「先頭の数件を除外して判定する」といったロジックを記述できます。
コード例:偶数月のデータのみを抽出
1月から12月までの売上データがある配列から、偶数月(2月、4月…)かつ売上が目標を超えている月を抽出する例です。配列のインデックスは0から始まるため、インデックスが奇数(1, 3, 5…)の場所が、実際には2月、4月、6月(偶数月)に相当します。
using System;
using System.Collections.Generic;
using System.Linq;
public class WhereIndexExample
{
public static void Main()
{
// 1月から12月までの月間売上データ
int[] monthlySales =
{
120, 150, 90, 200, 110,
300, 140, 160, 95, 210,
180, 250
};
// 条件:
// 1. 偶数月であること (index は 0 始まりなので、index が奇数のときが偶数月)
// 2. 売上が 150 以上であること
var targetSales = monthlySales.Where((sales, index) =>
{
bool isEvenMonth = (index % 2 != 0); // 1, 3, 5... (2月, 4月, 6月...)
return isEvenMonth && sales >= 150;
});
Console.WriteLine("--- 条件に一致する売上データ ---");
foreach (var sales in targetSales)
{
Console.WriteLine($"売上: {sales}");
}
}
}
出力結果:
--- 条件に一致する売上データ ---
売上: 150
売上: 200
売上: 300
売上: 160
売上: 210
売上: 250
インデックス1(2月)の150、インデックス3(4月)の200などが抽出されています。
遅延実行(Deferred Execution)について
LINQのWhereメソッドの重要な特性として、「遅延実行」があります。
Whereメソッドを呼び出した時点では、フィルタリング処理は実行されません。戻り値であるクエリ(IEnumerable<T>)をforeachループで回した時や、ToList()やToArray()などを呼び出して実体化させた瞬間に、初めてフィルタリングが実行されます。
var numbers = new List<int> { 1, 2, 3 };
var query = numbers.Where(n => n > 1); // ここではまだ実行されない
numbers.Add(4); // リストに要素を追加
// ここで初めて実行されるため、後から追加した '4' も結果に含まれる
foreach (var n in query)
{
Console.WriteLine(n); // 2, 3, 4 が出力される
}
この特性を理解しておくことは、データの整合性を保つ上で非常に重要です。
まとめ
Where(x => ...): コレクションから条件を満たす要素を抽出する基本的な方法です。Where((x, index) => ...): 要素の値だけでなく、インデックス(位置)も条件に含めることができます。- 遅延実行: クエリは定義時ではなく、列挙時(
foreachなど)に実行されます。
WhereメソッドはLINQの中でも最も頻繁に使用されるメソッドの一つです。インデックスを活用する方法も含めて習得することで、より柔軟なデータ操作が可能になります。
