C# の LINQ で範囲を取る Take と Skip
Take と Skip はシーケンスの一部を切り出すメソッドだ。ページネーションや部分取得によく使われる。
Take:先頭からN件取得
先頭から指定した数の要素を取得する。
int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
var firstThree = numbers.Take(3);
foreach (var n in firstThree)
{
Console.Write($"{n} "); // 1 2 3
}元のシーケンスより多い数を指定しても、存在する分だけ返る。例外は発生しない。
int[] numbers = { 1, 2, 3 };
var result = numbers.Take(10); // 1, 2, 3(3件しかない)Skip:先頭からN件スキップ
先頭から指定した数の要素を飛ばし、残りを返す。
int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
var afterThree = numbers.Skip(3);
foreach (var n in afterThree)
{
Console.Write($"{n} "); // 4 5 6 7 8 9 10
}全要素数より多くスキップすると、空のシーケンスが返る。
int[] numbers = { 1, 2, 3 };
var result = numbers.Skip(10); // 空
Console.WriteLine(result.Any()); // Falseページネーション
Skip と Take を組み合わせると、ページング処理ができる。
var allProducts = GetAllProducts(); // 全商品を取得
int pageSize = 10;
int pageNumber = 3; // 3ページ目(0から始める場合は2)
var page = allProducts
.Skip((pageNumber - 1) * pageSize)
.Take(pageSize);
foreach (var product in page)
{
Console.WriteLine(product.Name);
}3ページ目(21〜30件目)を取得している。
Skip
指定した数の要素を飛ばす
Take
指定した数の要素を取る
TakeLast / SkipLast
末尾からの操作には TakeLast と SkipLast を使う。
int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
var lastThree = numbers.TakeLast(3); // 8, 9, 10
var exceptLast = numbers.SkipLast(3); // 1, 2, 3, 4, 5, 6, 7TakeLast は末尾からN件、SkipLast は末尾のN件を除いた残りを返す。
TakeWhile / SkipWhile
条件を満たす間だけ取得・スキップすることもできる。
int[] numbers = { 1, 2, 3, 4, 5, 1, 2, 3 };
// 3より小さい間だけ取得
var takeResult = numbers.TakeWhile(n => n < 3); // 1, 2
// 3より小さい間はスキップ
var skipResult = numbers.SkipWhile(n => n < 3); // 3, 4, 5, 1, 2, 3TakeWhile は条件を満たさない要素が出た時点で終了する。SkipWhile は条件を満たさなくなった時点から残りをすべて返す。
注意点として、一度条件を満たさなくなったら、その後は条件を再評価しない。
int[] numbers = { 1, 2, 5, 1, 2 };
var result = numbers.SkipWhile(n => n < 3);
// 結果: 5, 1, 2
// (後半の 1, 2 も含まれる)範囲演算子(C# 8.0 以降)
C# 8.0 以降では、配列に対して範囲演算子を使う方法もある。
int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
// Take(3) と同じ
var firstThree = numbers[..3]; // 1, 2, 3
// Skip(3) と同じ
var afterThree = numbers[3..]; // 4, 5, 6, 7, 8, 9, 10
// Skip(2).Take(4) と同じ
var middle = numbers[2..6]; // 3, 4, 5, 6範囲演算子は配列に対してのみ使え、IEnumerable<T> には使えない。また、即座に新しい配列を作成する(即時評価)。
Chunk(.NET 6 以降)
シーケンスを指定サイズのグループに分割する。
int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
var chunks = numbers.Chunk(3);
foreach (var chunk in chunks)
{
Console.WriteLine(string.Join(", ", chunk));
}
// 1, 2, 3
// 4, 5, 6
// 7, 8, 9
// 10バッチ処理やページング処理に便利だ。
実用例:ランキング表示
var scores = new List<Player>
{
new Player { Name = "Alice", Score = 950 },
new Player { Name = "Bob", Score = 1200 },
new Player { Name = "Charlie", Score = 800 },
new Player { Name = "Diana", Score = 1100 },
new Player { Name = "Eve", Score = 1000 }
};
// 上位3名を表示
var topThree = scores
.OrderByDescending(p => p.Score)
.Take(3);
int rank = 1;
foreach (var player in topThree)
{
Console.WriteLine($"{rank++}. {player.Name}: {player.Score}");
}
// 1. Bob: 1200
// 2. Diana: 1100
// 3. Eve: 1000OrderByDescending で並べ替えてから Take で上位を切り出す。よくあるパターンだ。