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


84

startIndex및 a 가있는 경우 LINQ에서 컬렉션을 어떻게 페이지 로 이동 count합니까?

답변:


43

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


64

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

var query = from i in ideas
            select i;

var paggedCollection = query.Skip(startIndex).Take(count);

3
나는 이런 일을해도 괜찮다고 믿는다. 그는 답이 있을지 모르지만 다른 사람들이 무엇을 생각 해낼 수 있는지보고 싶어 할 수도 있습니다.
Outlaw Programmer

11
이것은 원래 StackOverflow 베타 기간의 첫날에 게시되었으므로 기사 ID는 66입니다. Jeff를 위해 시스템을 테스트하고있었습니다. 게다가 때로는 베타 테스트에서 나오는 일반적인 테스트 쓰레기 대신 유용한 정보처럼 보였습니다.
Nick Berardi

14

나는 리피터로 나만의 페이지를 만들어야했기 때문에 다른 사람들과는 약간 다르게 해결했습니다. 그래서 저는 먼저 제가 가지고있는 아이템 컬렉션을위한 페이지 번호 컬렉션을 만들었습니다.

// 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
    }
}

10

이 질문은 다소 오래되었지만 전체 절차 (사용자 상호 작용 포함)를 보여주는 페이징 알고리즘을 게시하고 싶었습니다.

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의 인터페이스를 직접 사용하는 변형 은 대신 마지막 논리 페이지의 끝 위치를 기억하므로 명시적인 건너 뛰기가 필요하지 않으며 부작용이 반복되지 않습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.