Підказка колекції за допомогою LINQ


Відповіді:


43

Кілька місяців тому я написав допис у блозі про вільні інтерфейси та LINQ, який використовував метод розширення IQueryable<T>та інший клас, щоб запропонувати наступний природний спосіб розміщення сторінок на колекціях LINQ.

var query = from i in ideas
            select i;
var pagedCollection = query.InPagesOf(10);
var pageOfIdeas = pagedCollection.Page(2);

Ви можете отримати код зі сторінки галереї кодів MSDN: трубопроводи, фільтри, API Fluent та LINQ to SQL .


64

Це дуже просто з методами Skipі Takeрозширення.

var query = from i in ideas
            select i;

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

3
Я вважаю, що робити щось подібне нормально. У нього може бути відповідь, але, можливо, він хоче побачити, що можуть придумати й інші люди.
Програміст поза законом

11
Це спочатку було опубліковано протягом першого дня бета-періоду StackOverflow, тобто 66 для ідентифікатора статті. Я тестував систему для Джеффа. Плюс здавалося корисною інформацією замість звичайного тестового лайна, який іноді виходить із бета-тестування.
Нік Берарді,

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>). Ось як це можна зробити, використовуючи 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
    }
}

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+, ви побачите, що всі побічні ефекти 10-30 виконуються знову, перш ніж почати ітерацію 40+. Варіант, що використовує IEnumerableбезпосередньо інтерфейс, замість цього запам'ятає позицію кінця останньої логічної сторінки, тому не потрібно явного пропуску, а побічні ефекти не будуть повторюватися.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.