どのようにあなたが持っていることを考えるとLINQでコレクションをあなたのページを行うstartIndex
とcount
?
回答:
数か月前、Fluent InterfacesとLINQについてのブログ投稿を書きました。これは、拡張メソッドIQueryable<T>
と別のクラスを使用して、LINQコレクションをページ付けする次の自然な方法を提供します。
var query = from i in ideas
select i;
var pagedCollection = query.InPagesOf(10);
var pageOfIdeas = pagedCollection.Page(2);
コードは、MSDNコードギャラリーページから入手できます:パイプライン、フィルター、Fluent API、およびLINQ toSQL。
Skip
およびTake
拡張メソッドを使用すると非常に簡単です。
var query = from i in ideas
select i;
var paggedCollection = query.Skip(startIndex).Take(count);
リピーターを使って自分でパジネーターを作らなければならなかったので、他の人とは少し違った方法でこれを解決しました。それで、私は最初に私が持っているアイテムのコレクションのためにページ番号のコレクションを作りました:
// assumes that the item collection is "myItems"
int pageCount = (myItems.Count + PageSize - 1) / PageSize;
IEnumerable<int> pageRange = Enumerable.Range(1, pageCount);
// pageRange contains [1, 2, ... , pageCount]
これを使用して、アイテムコレクションを「ページ」のコレクションに簡単に分割できました。この場合のページは、単なるアイテムのコレクションです(IEnumerable<Item>
)。これは、上で作成したものからインデックスを選択することSkip
とTake
一緒にそれを行う方法ですpageRange
。
IEnumerable<IEnumerable<Item>> pageRange
.Select((page, index) =>
myItems
.Skip(index*PageSize)
.Take(PageSize));
もちろん、各ページを追加のコレクションとして処理する必要がありますが、たとえば、リピーターをネストしている場合、これは実際には簡単に処理できます。
ワンライナーTLDRのバージョンは、このようになります:
var pages = Enumerable
.Range(0, pageCount)
.Select((index) => myItems.Skip(index*PageSize).Take(PageSize));
これは次のように使用できます。
for (Enumerable<Item> page : pages)
{
// handle page
for (Item item : page)
{
// handle item in page
}
}
この質問はやや古いですが、手順全体(ユーザーの操作を含む)を示すページングアルゴリズムを投稿したいと思いました。
const int pageSize = 10;
const int count = 100;
const int startIndex = 20;
int took = 0;
bool getNextPage;
var page = ideas.Skip(startIndex);
do
{
Console.WriteLine("Page {0}:", (took / pageSize) + 1);
foreach (var idea in page.Take(pageSize))
{
Console.WriteLine(idea);
}
took += pageSize;
if (took < count)
{
Console.WriteLine("Next page (y/n)?");
char answer = Console.ReadLine().FirstOrDefault();
getNextPage = default(char) != answer && 'y' == char.ToLowerInvariant(answer);
if (getNextPage)
{
page = page.Skip(pageSize);
}
}
}
while (getNextPage && took < count);
ただし、パフォーマンス後、本番コードではすべてパフォーマンス後の場合は、上記のようにLINQのページングを使用するのではなく、IEnumerator
ページングを自分で実装するための基礎を使用する必要があります。実際のところ、これは上記のLINQアルゴリズムと同じくらい単純ですが、よりパフォーマンスが高くなります。
const int pageSize = 10;
const int count = 100;
const int startIndex = 20;
int took = 0;
bool getNextPage = true;
using (var page = ideas.Skip(startIndex).GetEnumerator())
{
do
{
Console.WriteLine("Page {0}:", (took / pageSize) + 1);
int currentPageItemNo = 0;
while (currentPageItemNo++ < pageSize && page.MoveNext())
{
var idea = page.Current;
Console.WriteLine(idea);
}
took += pageSize;
if (took < count)
{
Console.WriteLine("Next page (y/n)?");
char answer = Console.ReadLine().FirstOrDefault();
getNextPage = default(char) != answer && 'y' == char.ToLowerInvariant(answer);
}
}
while (getNextPage && took < count);
}
説明:Skip()
「カスケード方式」で複数回使用することの欠点は、最後にスキップされた反復の「ポインター」が実際には格納されないことです。-代わりに、元のシーケンスにスキップ呼び出しがフロントロードされます。これにより、すでに「消費された」ページが何度も「消費」されます。-シーケンスを作成してideas
副作用が発生する場合は、自分で証明できます。-> 10-20と20-30をスキップして、40 +を処理したい場合でも、40 +の反復を開始する前に、10-30のすべての副作用が再度実行されるのがわかります。IEnumerable
のインターフェイスを直接使用するバリアントは、代わりに最後の論理ページの終わりの位置を記憶するため、明示的なスキップは不要であり、副作用は繰り返されません。