LINQ - Limitando dados com Skip() e Take()


 Hoje vamos recordar como podemos limitar o acesso a dados usando os métodos Skip() e Take() do LINQ.

Hoje vamos recordar o uso dos métodos Skip() e Take() para limitar a quantidade de dados com os quais desejamos trabalhar.

Isso é útil ao usar um banco de dados como fonte de dados, porque pode envolver grandes quantidades de registros, cuja operação consome recursos.



Para otimizar a busca e o acesso aos dados podemos usar os métodos Take() e Skip() e, geralmente fazemos isso de forma combinada; esses métodos são ótimos para realizar a paginação de dados em um site. Eles costumam ser usados juntos, mas também podem ser usados sozinhos.

Usando um exemplo bem simples vamos criar uma lista de nomes e usar esses métodos para obter valores da lista.

            List<string> lista = new List<string>()
            {
                "Jose Soares",
                "Josué Santos",
                "Jason Silva",
                "Jonas Silveira",
                "Jessica Sobral",
            };

Vamos supor que eu quero pular o primeiro nome e obter os dois nomes seguintes.

Usando uma combinação de Skip() com Take() podemos escrever o código abaixo:

using System;
using System.Collections.Generic;
using System.Linq;
namespace CShp_TakeSkip
{
    class Program
    {
        static void Main(string[] args)
        {
            List<string> lista = new List<string>()
            {
                "Jose Soares",
                "Josué Santos",
                "Jason Silva",
                "Jonas Silveira",
                "Jessica Sobral",
            };
            var nomes = lista.Skip(1).Take(2).ToList();
            foreach (var nome in nomes)
                Console.WriteLine(nome);
            Console.ReadLine();
        }
    }
}

Analisando a consulta criada temos que:

  1. lista.Skip(1) - pula o primeiro nome
  2. Take(2) - pega os dois próximos nomes

Executando o projeto o resultado é visto a seguir:

Podemos realizar uma demonstração melhor usando mais dados.

Vamos usar como fonte de dados os dados retornados pela página : http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml

Esta página retorna o XML abaixo que representa as taxas de câmbio diária para uma data:

<gesmes:Envelope>
<gesmes:subject>Reference rates</gesmes:subject>
<gesmes:Sender>
<gesmes:name>European Central Bank</gesmes:name>
</gesmes:Sender>
<Cube>
  <Cube time="2020-11-03">
    <Cube currency="USD" rate="1.1702"/>
    <Cube currency="JPY" rate="122.56"/>
    <Cube currency="BGN" rate="1.9558"/>
    <Cube currency="CZK" rate="26.913"/>
    <Cube currency="DKK" rate="7.4462"/>
    <Cube currency="GBP" rate="0.90042"/>
    <Cube currency="HUF" rate="363.82"/>
    <Cube currency="PLN" rate="4.5680"/>
    <Cube currency="RON" rate="4.8675"/>
    <Cube currency="SEK" rate="10.3835"/>
    <Cube currency="CHF" rate="1.0709"/>
    <Cube currency="ISK" rate="163.70"/>
    <Cube currency="NOK" rate="11.0103"/>
    <Cube currency="HRK" rate="7.5595"/>
    <Cube currency="RUB" rate="92.8975"/>
    <Cube currency="TRY" rate="9.9840"/>
    <Cube currency="AUD" rate="1.6406"/>
    <Cube currency="BRL" rate="6.6742"/>
    <Cube currency="CAD" rate="1.5385"/>
    <Cube currency="CNY" rate="7.8198"/>
    <Cube currency="HKD" rate="9.0694"/>
    <Cube currency="IDR" rate="16946.31"/>
    <Cube currency="ILS" rate="3.9970"/>
    <Cube currency="INR" rate="87.0695"/>
    <Cube currency="KRW" rate="1323.74"/>
    <Cube currency="MXN" rate="24.7581"/>
    <Cube currency="MYR" rate="4.8634"/>
    <Cube currency="NZD" rate="1.7531"/>
    <Cube currency="PHP" rate="56.584"/>
    <Cube currency="SGD" rate="1.5931"/>
    <Cube currency="THB" rate="36.329"/>
    <Cube currency="ZAR" rate="18.7681"/>
 </Cube>
</Cube>
</gesmes:Envelope>

Vamos acessar e percorrer os dados realizando a paginação com os métodos Skip() e Take().

using System;
using System.Globalization;
using System.Linq;
using System.Xml.Linq;

namespace Paginacao
{
    class Program
    {
        static void Main(string[] args)
        {
            CultureInfo usCulture = new CultureInfo("en-US");

            XDocument xDoc = XDocument.Load("http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml");
            var cubeNodes = xDoc.Descendants().Where(n => n.Name.LocalName == "Cube" && n.Attribute("currency") != null).ToList();

            var currencyRateItems = cubeNodes.Select(node => new
            {
                Currency = node.Attribute("currency").Value,
                Rate = double.Parse(node.Attribute("rate").Value, usCulture)
            });

            int pageSize = 10, contaPagina = 0;
            var pageItems = currencyRateItems.Take(pageSize);

            while (pageItems.Count() > 0)
            {
                foreach (var item in pageItems)
                    Console.WriteLine(item.Currency + ": " + item.Rate.ToString("N2", usCulture));
               

                Console.WriteLine("Pressione algo para obter os próximos itens...");

                Console.ReadKey();
                contaPagina++;
               

                // Aqui usamos os métodos Skip() e Take()
                pageItems = currencyRateItems.Skip(pageSize * contaPagina).Take(pageSize);

            }
            Console.WriteLine("Concluído");
        }
    }
}

Vamos entender o código:

1- Obtém as linhas com dados que desejamos exibir :

XDocument xDoc = XDocument.Load("http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml");

2- Obtém os valores para Currency e rate:

           var currencyRateItems = cubeNodes.Select(node => new
            {
                Currency = node.Attribute("currency").Value,
                Rate = double.Parse(node.Attribute("rate").Value, usCulture)
            });

3- Define o tamanho da página, o contador e retorna a primeira página contendo os itens para exibir:

    int pageSize = 10, contaPagina = 0;
    var pageItems = currencyRateItems.Take(pageSize);

4- Percorre o resultado e exibe os itens realizando  paginação com Skip e Take:

while (pageItems.Count() > 0)
{
    foreach (var item in pageItems)
        Console.WriteLine(item.Currency + ": " + item.Rate.ToString("N2", usCulture));

    Console.WriteLine("Pressione algo para obter os próximos itens...");

    Console.ReadKey();
    contaPagina++;

    // Aqui usamos os métodos Skip() e Take()
    pageItems = currencyRateItems.Skip(pageSize * contaPagina).Take(pageSize);

}

Executando o projeto teremos o resultado abaixo:

Embora sejam bem simples de usar, os métodos Skip() e Take() podem ser úteis em muitas situações como a paginação de dados.

Pegue o projeto aqui:  CShp_TakeSkip.zip

"Porque a palavra de Deus é viva e eficaz, e mais penetrante do que espada alguma de dois gumes, e penetra até à divisão da alma e do espírito, e das juntas e medulas, e é apta para discernir os pensamentos e intenções do coração."
Hebreus 4:12

Referências:


José Carlos Macoratti