startIndex
및 a 가있는 경우 LINQ에서 컬렉션을 어떻게 페이지 로 이동 count
합니까?
답변:
몇 달 전에 Fluent Interfaces 및 LINQ에 대한 블로그 게시물을 작성했습니다.이 블로그 게시물은 Extension Method on 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 및 LINQ to SQL) 에서 코드를 얻을 수 있습니다 .
Skip
및 Take
확장 메서드를 사용하면 매우 간단 합니다.
var query = from i in ideas
select i;
var paggedCollection = query.Skip(startIndex).Take(count);
나는 리피터로 나만의 페이지를 만들어야했기 때문에 다른 사람들과는 약간 다르게 해결했습니다. 그래서 저는 먼저 제가 가지고있는 아이템 컬렉션을위한 페이지 번호 컬렉션을 만들었습니다.
// 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>
). 이것은 당신이 사용하여 할 수있는 방법입니다 Skip
및 Take
로부터 인덱스를 선택과 함께 pageRange
만든 이상 :
IEnumerable<IEnumerable<Item>> pageRange
.Select((page, index) =>
myItems
.Skip(index*PageSize)
.Take(PageSize));
물론 각 페이지를 추가 컬렉션으로 처리해야하지만 예를 들어 반복 레이아웃을 중첩하는 경우 실제로 처리하기 쉽습니다.
한 줄 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
}
}
이 질문은 다소 오래되었지만 전체 절차 (사용자 상호 작용 포함)를 보여주는 페이징 알고리즘을 게시하고 싶었습니다.
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
의 인터페이스를 직접 사용하는 변형 은 대신 마지막 논리 페이지의 끝 위치를 기억하므로 명시적인 건너 뛰기가 필요하지 않으며 부작용이 반복되지 않습니다.