Paginare una raccolta con LINQ


86

Come si sfoglia una raccolta in LINQ dato che si dispone di a startIndexe a count?

Risposte:


43

Alcuni mesi fa ho scritto un post sul blog su Fluent Interfaces e LINQ che utilizzava un metodo di estensione IQueryable<T>e un'altra classe per fornire il seguente modo naturale di impaginare una raccolta LINQ.

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

È possibile ottenere il codice dalla pagina Raccolta codici MSDN: pipeline, filtri, API Fluent e LINQ to SQL .


64

È molto semplice con i metodi di estensione Skipe Take.

var query = from i in ideas
            select i;

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

3
Credo che vada bene fare qualcosa di simile. Potrebbe avere una risposta, ma forse vuole vedere cosa possono inventare anche gli altri.
Programmatore fuorilegge

11
Questo è stato originariamente pubblicato durante il primo giorno del periodo beta di StackOverflow, quindi il 66 per l'ID articolo. Stavo testando il sistema, per Jeff. Inoltre sembravano informazioni utili invece della solita merda di test che a volte esce dal beta test.
Nick Berardi

14

L'ho risolto in modo un po 'diverso rispetto a quello che hanno gli altri poiché dovevo creare il mio impaginatore, con un ripetitore. Quindi ho prima creato una raccolta di numeri di pagina per la raccolta di elementi che ho:

// 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]

Usando questo potrei facilmente suddividere la raccolta di elementi in una raccolta di "pagine". Una pagina in questo caso è solo una raccolta di elementi ( IEnumerable<Item>). Ecco come puoi farlo usando Skipe Takeinsieme alla selezione dell'indice dal pageRangecreato sopra:

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

Ovviamente devi gestire ogni pagina come una raccolta aggiuntiva, ma ad esempio, se stai annidando ripetitori, questo è effettivamente facile da gestire.


La versione TLDR di una riga sarebbe questa:

var pages = Enumerable
    .Range(0, pageCount)
    .Select((index) => myItems.Skip(index*PageSize).Take(PageSize));

Che può essere usato come questo:

for (Enumerable<Item> page : pages) 
{
    // handle page

    for (Item item : page) 
    {
        // handle item in page
    }
}

10

Questa domanda è piuttosto vecchia, ma volevo pubblicare il mio algoritmo di paging che mostra l'intera procedura (inclusa l'interazione dell'utente).

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);

Tuttavia, se stai cercando le prestazioni e nel codice di produzione, siamo tutti dopo le prestazioni, non dovresti usare il paging di LINQ come mostrato sopra, ma piuttosto il sottostante IEnumeratorper implementare il paging da solo. È un dato di fatto, è semplice come l'algoritmo LINQ mostrato sopra, ma più performante:

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);
}

Spiegazione: Lo svantaggio dell'utilizzo Skip()per più volte in un "modo a cascata" è che non memorizzerà realmente il "puntatore" dell'iterazione, dove è stato saltato l'ultima volta. - Invece la sequenza originale sarà caricata in anticipo con chiamate di salto, che porteranno a "consumare" le pagine già "consumate" più e più volte. - Puoi provarlo tu stesso, quando crei la sequenza in ideasmodo che produca effetti collaterali. -> Anche se hai saltato 10-20 e 20-30 e desideri elaborare 40+, vedrai tutti gli effetti collaterali di 10-30 essere eseguiti di nuovo, prima di iniziare a iterare 40+. La variante che utilizza IEnumerabledirettamente l'interfaccia di, ricorderà invece la posizione della fine dell'ultima pagina logica, quindi non è necessario alcun salto esplicito e gli effetti collaterali non verranno ripetuti.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.