Jak przeglądać kolekcję w LINQ, biorąc pod uwagę, że masz startIndex
i count
?Paging kolekcja z LINQ
Odpowiedz
Kilka miesięcy temu napisałem blogu o Fluent interfejsów i LINQ, który wykorzystał metodę rozszerzenia na IQueryable<T>
i innej klasy, aby podać następujące naturalny sposób Stronicowanie zbiór LINQ.
var query = from i in ideas
select i;
var pagedCollection = query.InPagesOf(10);
var pageOfIdeas = pagedCollection.Page(2);
Możesz pobrać kod z Galerii kodów MSDN Strona: Pipelines, Filters, Fluent API and LINQ to SQL.
Jest to bardzo proste z metodami rozszerzenia Skip
i Take
.
var query = from i in ideas
select i;
var paggedCollection = query.Skip(startIndex).Take(count);
To pytanie jest nieco stare, ale chciałem opublikować mój algorytm stronicowania, który pokazuje całą procedurę (w tym interakcje użytkownika).
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);
Jednakże, jeśli jesteś po wydajności, a w kodzie produkcyjnym, wszyscy jesteśmy po wykonaniu, nie należy używać stronicowania LINQ, jak przedstawiono powyżej, lecz instrumentem bazowym IEnumerator
wdrożyć stronicowania siebie. W rzeczywistości jest to tak proste jak w Linq algorytmu przedstawionego powyżej, ale bardziej wydajnych:
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);
}
Objaśnienie: Wadą stosowania Skip()
do wielu razy w „kaskadowego” sposób jest to, że nie będzie tak naprawdę nie przechowuje "wskaźnika" iteracji, gdzie był ostatnio pomijany. - Zamiast tego oryginalna sekwencja zostanie załadowana z wyprzedzeniem z pominięciem wywołań, co doprowadzi do "konsumowania" już "zużytych" stron w kółko. - Możesz to udowodnić samodzielnie, gdy utworzysz sekwencję ideas
, dzięki czemu uzyska ona efekty uboczne. -> Nawet jeśli pominąłeś 10-20 i 20-30 i chcesz przetworzyć 40+, zobaczysz wszystkie efekty uboczne 10-30, które zostaną ponownie wykonane, zanim zaczniesz powtarzać 40+. Wariant wykorzystujący bezpośrednio interfejs IEnumerable
zapamięta położenie końca ostatniej strony logicznej, więc nie jest konieczne żadne pominięcie jawne, a efekty uboczne nie będą powtarzane.
Rozwiązałem to nieco inaczej niż inni, ponieważ musiałem stworzyć własny paginator z repeaterami. Więc raz pierwszy zbiór numerów stron do gromadzenia przedmiotów, które mam:
// 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]
Używanie tego mogę łatwo podzielić kolekcję element do kolekcji „stron”. Strona w tym przypadku to tylko zbiór przedmiotów (IEnumerable<Item>
). W ten sposób można zrobić to za pomocą Skip
i Take
wraz z wybierając indeks z pageRange
utworzonego powyżej:
IEnumerable<IEnumerable<Item>> pageRange
.Select((page, index) =>
myItems
.Skip(index*PageSize)
.Take(PageSize));
oczywiście trzeba obsłużyć każdą stronę jako dodatkowy kolekcji ale np jeśli budujesz wzmacniacze, to jest to łatwe w obsłudze.
jedno-liner TLDR wersja byłaby to:
var pages = Enumerable
.Range(0, pageCount)
.Select((index) => myItems.Skip(index*PageSize).Take(PageSize));
które mogą być wykorzystane jako to:
for (Enumerable<Item> page : pages)
{
// handle page
for (Item item : page)
{
// handle item in page
}
}
wierzę, że to OK, aby zrobić coś takiego. Mógł mieć odpowiedź, ale może chce zobaczyć, co też mogą wymyślić inni ludzie. –
Ten został pierwotnie opublikowany w pierwszym dniu okresu beta StackOverflow, a więc 66 dla identyfikatora artykułu. Testowałem system, dla Jeffa. Poza tym wydawało się przydatne informacje zamiast zwykłych bzdur testowych, które czasami pochodzą z testów beta. –