Jak przeglądasz kolekcję w LINQ, biorąc pod uwagę, że masz a startIndex
i count
?
84
Kilka miesięcy temu napisałem post na blogu o interfejsach Fluent i LINQ, w których zastosowano metodę rozszerzenia IQueryable<T>
i inną klasę, aby zapewnić następujący naturalny sposób podziału na strony kolekcji LINQ.
var query = from i in ideas
select i;
var pagedCollection = query.InPagesOf(10);
var pageOfIdeas = pagedCollection.Page(2);
Możesz pobrać kod ze strony MSDN Code Gallery: Pipelines, Filters, Fluent API i LINQ to SQL .
Jest to bardzo proste dzięki metodom Skip
i Take
rozszerzającym.
var query = from i in ideas
select i;
var paggedCollection = query.Skip(startIndex).Take(count);
Rozwiązałem to trochę inaczej niż to, co mają inni, ponieważ musiałem stworzyć własny paginator, z repeaterem. Więc najpierw utworzyłem zbiór numerów stron dla kolekcji elementó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żywając tego, mogłem łatwo podzielić kolekcję elementów na zbiór „stron”. Strona w tym przypadku to po prostu zbiór elementów (
IEnumerable<Item>
). Oto jak możesz to zrobić używającSkip
iTake
razem z wyborem indeksu zpageRange
utworzonego powyżej:IEnumerable<IEnumerable<Item>> pageRange .Select((page, index) => myItems .Skip(index*PageSize) .Take(PageSize));
Oczywiście musisz traktować każdą stronę jako dodatkową kolekcję, ale np. Jeśli zagnieżdżasz repetytory, jest to w rzeczywistości łatwe w obsłudze.
Jedno-liner TLDR wersja byłoby to:
var pages = Enumerable .Range(0, pageCount) .Select((index) => myItems.Skip(index*PageSize).Take(PageSize));
Które można wykorzystać w ten sposób:
for (Enumerable<Item> page : pages) { // handle page for (Item item : page) { // handle item in page } }
źródło
To pytanie jest dość stare, ale chciałem opublikować mój algorytm stronicowania, który pokazuje całą procedurę (w tym interakcję 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);
Jeśli jednak zależy Ci na wydajności, a w kodzie produkcyjnym wszyscy zależy nam na wydajności, nie powinieneś używać stronicowania LINQ, jak pokazano powyżej, ale raczej podstawy
IEnumerator
do samodzielnego zaimplementowania stronicowania. W rzeczywistości jest tak prosty, jak algorytm LINQ pokazany powyżej, ale bardziej wydajny: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ą
Skip()
wielokrotnego używania w „sposób kaskadowy” jest to, że tak naprawdę nie przechowuje „wskaźnika” iteracji, w którym był ostatnio pomijany. - Zamiast tego oryginalna sekwencja będzie poprzedzona wywołaniami pominięcia, co doprowadzi do ciągłego „konsumowania” już „wykorzystanych” stron. - Możesz to udowodnić, tworząc sekwencjęideas
tak, aby przynosiła efekty uboczne. -> Nawet jeśli pominąłeś 10-20 i 20-30 i chcesz przetworzyć 40+, zobaczysz, że wszystkie efekty uboczne 10-30 są wykonywane ponownie, zanim zaczniesz iterować 40+. Wariant korzystającyIEnumerable
bezpośrednio z interfejsu zapamięta pozycję końca ostatniej logicznej strony, więc nie jest potrzebne żadne wyraźne przeskakiwanie, a efekty uboczne nie będą się powtarzać.źródło