Come si sfoglia una raccolta in LINQ dato che si dispone di a startIndex
e a count
?
Come si sfoglia una raccolta in LINQ dato che si dispone di a startIndex
e a count
?
Risposte:
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 .
È molto semplice con i metodi di estensione Skip
e Take
.
var query = from i in ideas
select i;
var paggedCollection = query.Skip(startIndex).Take(count);
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 Skip
e Take
insieme alla selezione dell'indice dal pageRange
creato 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
}
}
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 IEnumerator
per 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 ideas
modo 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 IEnumerable
direttamente 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.