[linq] LINQ를 사용하여 컬렉션 페이징

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) 에서 코드를 얻을 수 있습니다 .


답변

SkipTake확장 메서드를 사용하면 매우 간단 합니다.

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>). 이것은 당신이 사용하여 할 수있는 방법입니다 SkipTake로부터 인덱스를 선택과 함께 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의 인터페이스를 직접 사용하는 변형 은 대신 마지막 논리 페이지의 끝 위치를 기억하므로 명시적인 건너 뛰기가 필요하지 않으며 부작용이 반복되지 않습니다.


답변