2008-08-01 17 views

答えて

34

数ヶ月前に私は、LINQのコレクションをページ付け、次の自然な方法を提供するIQueryable<T>と別のクラスに拡張メソッドを使用流暢インターフェイスとLINQに関するブログ記事を書きました。

var query = from i in ideas 
      select i; 
var pagedCollection = query.InPagesOf(10); 
var pageOfIdeas = pagedCollection.Page(2); 

コードは、MSDNコードギャラリーページから入手できます。Pipelines, Filters, Fluent API and LINQ to SQL

59

SkipTake拡張メソッドでは非常に簡単です。

var query = from i in ideas 
      select i; 

var paggedCollection = query.Skip(startIndex).Take(count); 
+3

私はそれがこのような何かをするためにOKだと信じています。彼は答えを持っているかもしれないが、多分彼は他の人々が同様に思い付くことができるものを見たいと思うかもしれない。 –

+11

これは、StackOverflowのベータ期間の最初の日、つまり記事IDの66日に最初に掲載されました。私はジェフのためにシステムをテストしていました。それは、ベータテストから時々出てくる通常のテストの間違いの代わりに、役に立つ情報のように思えました。 –

6

この質問は多少古くなっていますが、手順全体(ユーザーとの対話を含む)を示すページングアルゴリズムを投稿したいと考えました。あなたは、パフォーマンスの後であり、生産コードで、我々はすべてのパフォーマンス後にしている場合

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のインターフェースを直接使用するバリアントは、最後の論理ページの終わりの位置を覚えているので、明示的にスキップする必要はなく、副作用は繰り返されません。

10

私はリピーターで自分のページネーターを作っていたので、他のものと少し違って解決しました。だから私は、最初に私が持っているアイテムのコレクションのためのページ番号のコレクションを作っ:

// 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>)です。あなたは追加のコレクションとして、各ページを処理する必要がなく、例えばもちろん

IEnumerable<IEnumerable<Item>> pageRange 
    .Select((page, index) => 
     myItems 
      .Skip(index*PageSize) 
      .Take(PageSize)); 

:これは、上記で作成したpageRangeからインデックスを選択して一緒にSkipTakeを使用してそれを行うことができる方法でありますリピーターを入れ子にしている場合は、実際には扱いやすいです。


ワンライナー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 
    } 
} 
関連する問題