Pregunta

¿Cómo se puede desplazarse por una colección en LINQ dado que se tiene un startIndex y un count?

¿Fue útil?

Solución

Hace unos meses escribí una publicación de blog sobre Fluent Interfaces y LINQ que usaba un método de extensión en IQueryable<T> y otra clase para proporcionar la siguiente forma natural de paginar una colección LINQ.

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

Puede obtener el código en la página de la galería de códigos de MSDN: Canalizaciones, filtros, API fluida y LINQ to SQL.

Otros consejos

Es muy sencillo con el Skip y Take métodos de extensión.

var query = from i in ideas
            select i;

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

Resolví esto un poco diferente a lo que tienen los demás, ya que tuve que hacer mi propio paginador, con un repetidor.Así que primero hice una colección de números de página para la colección de elementos que tengo:

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

Con esto, podría dividir fácilmente la colección de elementos en una colección de "páginas".Una página en este caso es solo una colección de elementos (IEnumerable<Item>).Así es como puedes hacerlo usando Skip y Take junto con la selección del índice de la pageRange creado arriba:

IEnumerable<IEnumerable<Item>> pageRange
    .Select((page, index) => 
        myItems
            .Skip(index*PageSize)
            .Take(PageSize));

Por supuesto, debe manejar cada página como una colección adicional, pero p.e.Si está anidando repetidores, esto es realmente fácil de manejar.


El TLDR de una sola línea La versión sería esta:

var pages = Enumerable
    .Range(0, pageCount)
    .Select((index) => myItems.Skip(index*PageSize).Take(PageSize));

Que se puede utilizar así:

for (Enumerable<Item> page : pages) 
{
    // handle page

    for (Item item : page) 
    {
        // handle item in page
    }
}

Esta pregunta es algo antigua, pero quería publicar mi algoritmo de paginación que muestra todo el procedimiento (incluida la interacción del usuario).

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);

Sin embargo, si lo que busca es rendimiento, y en el código de producción todos buscamos rendimiento, no debería utilizar la paginación de LINQ como se muestra arriba, sino la paginación subyacente. IEnumerator para implementar la paginación usted mismo.De hecho, es tan simple como el algoritmo LINQ que se muestra arriba, pero con más rendimiento:

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);
}

Explicación:La desventaja de usar Skip() para varias veces en "forma de cascada" es que realmente no almacenará el "puntero" de la iteración, donde se omitió por última vez.- En lugar de eso, la secuencia original se cargará al principio con llamadas de omisión, lo que llevará a "consumir" las páginas ya "consumidas" una y otra vez.- Puedes demostrarlo tú mismo, cuando crees la secuencia. ideas para que produzca efectos secundarios.-> Incluso si se ha saltado 10-20 y 20-30 y desea procesar 40+, verá que todos los efectos secundarios de 10-30 se ejecutan nuevamente, antes de comenzar a iterar 40+.La variante usando IEnumerableLa interfaz de directamente recordará la posición del final de la última página lógica, por lo que no es necesario omitirla explícitamente y los efectos secundarios no se repetirán.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top