domingo, 26 de julio de 2009

LINQ to Object en 10 minutos


LINQ to Object nos permite a los desarrolladores .NET escribir "consultas" sobre las colecciones de objetos.

Tradicionalmente, el trabajo con colecciones de objetos ha sido muy utilizado, pero nos obligaba a escribir un montón de código para recorrer bucles foreach complejos que especifican cómo recuperar los datos de una colección, y así trabajar con los elementos de las listas, o para hacer conteos o sumas acumuladas o cualquier otra operación.

LINQ nos facilita todas estas acciones, basta con escribe el código declarativo que describe lo que se desea recuperar.

Pero echemos un vistazo de algunos ejemplos que nos ayuden a entender la sintaxis básica.
public static void Linq1()
{
  int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
  var lowNums =
    from n in numbers
    where n < 5
    select n;

  Console.WriteLine("Numbers < 5:");
  
  foreach (var x in lowNums)
  {
    Console.WriteLine(x);
  }
}
Con el ejemplo anterior, vemos que podemos filtrar una lista, en este caso filtramos una lista de numbers, dejando solo aquellos que son menores que 5, y el resultado será guardado en lowNums.

Como hemos visto es muy intuitivo el modo de escribir las “consultas”. Pero veamos otros ejemplos que nos muestren las cosas que podemos hacer.
public static void LinqSum1()
{
  int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
  double numSum = numbers.Sum();

  Console.WriteLine("La suma de los numeros es: {0}.", numSum);
}
También es posible hacer filtros en la suma deseada, veamos el siguiente ejemplo que lo explica mejor:
double numSum = numbers.Where(n =>; n > 5).Sum();
Acá el resultado según el juego de datos anterior seria 10. Debido a que estaríamos sumando todos los números que cumplen con la condición de que el numero sea menor que 5.

Veamos otras cosas que podemos hacer, pero antes debemos tener en cuenta que aunque la forma de escribir consultas LINQ es muy similar a las de cualquier base de datos relacional SQL, existe una diferencia y es sobre el orden en que aparecen los operadores. Pero sigamos viendo ejemplos:
public static void LinqOrderBy()
{
  List<Producto> productos = GetProductList();

  var sortedProducts =
    from p in productos
    where p.Categoria = "2"
    orderby p.Cantidad descending
    select p;
}

En este filtramos todos los productos cuya categoría sea “2“ y lo ordenamos en orden descendente por la propiedad Cantidad.

Compliquemos un poco más la consulta de forma que tome los datos de 2 listas distintas y devuelva un conjunto de elementos anónimos.

public static void LinqJoin()
{
  List<Producto> productos = GetProductList();
  List<Categoria> categorias = GetCategoryList();

  var elementos =
    from p in productos
    join c in categorias on p.Categoria equals c.Categoria
    where p.Categoria = "2"
    orderby p.Cantidad descending
    select new {
      ProductoNombre = p.Descripcion,
      ProductoNombre = p.Descripcion,
      Existencia = p.Cantidad
      };
}

En este ejemplo vemos como se relacionan las dos listas de elementos por el campo común “Categoria” y se obtiene como resultado una lista de “elementos” que es además de tipo anónimo.

LINQ “ANY"

Veamos un ejemplo. Imaginemos que tengo un:
String[n] pricesType, que contiene una lista o array de los distintos tipos de precios de un determinado elemento. Y quiero saber si un determinado tipo de precio esta en dicha lista (por ejemplo el tipo de precio "L").
Para ello podría usar Any de la siguiente manera:

bool result = pricesType.Any(s => s.Contains("L"));
//o también
bool result = pricesType.Any(s => s == "L");
 

RESUMEN LINQ TO OBJECT

Con esta lista de ejemplos he intentado trasmitir la idea básica de LINQ to Object, espero les halla sido de ayuda.

Las consultas LINQ ofrecen tres ventajas principales respecto a los bucles foreach tradicionales:

  1. Son más concisas y legibles, sobre todo al filtrar varias condiciones.
  2. Proporcionan funcionalidad eficaz de filtrado, ordenación y agrupación con código de aplicación mínimo.

  3. Se pueden trasladar a otros orígenes de datos con pocas o ningunas modificaciones.

En general, cuanto más compleja sea la operación que se deba realizar con los datos, observará un número mayor de ventajas al utilizar LINQ en lugar de las técnicas de iteración convencionales.


Notas:

En unos días continuaré escribiendo sobre LINQ, profundizaré LINQ to SQL y LINQ to XML, que son los dos temas, además de LINQ to Object que más me han solicitado; si tienes algún interés particular no dudes en dejarme tú comentario o enviarme un mail.

Puede utilizar LINQ para consultar cualquier colección enumerable, como List<(Of <(T>)>), Array o Dictionary<(Of <(TKey, TValue>)>). La colección puede estar definida por el usuario o ser devuelta por una API de .NET Framework.


Artículos Relacionados:

5 comentarios:

  1. result =
    ( from s in txtExcludeCompany.Text.Split(';')
    where !string.IsNullOrEmpty(s)
    select s.Trim()
    ).ToList();

    Con este ejemplo devolvemos una lista de "String" con todas las oraciones o palabras que aparecen en "txtExcludeCompany" y que estan separadas por ";"

    ResponderEliminar
  2. Hola, he revisado el blog y quiero compartir con ustedes tres variantes de implementacion de la funcion Existe() y siguiendo los ejemplos de un listado de Categorias que en mi caso contendria un listado de sus productos y agrego esta funcion dentro de la clase categoria para saber si existe entre sus Productos aquel que contenga en su nombre "Revista":
    //Implementacion Variante#1
    public bool Existe()
    {
    //Lista de Categorias
    categorias = GetCategoryList();
    return ((from itemCategoria in categorias
    where (itemCategoria.Productos != null) && (itemCategoria.Productos.NombreProducto.Contains("Revista"))
    select itemCategoria).Count() != 0);
    }

    //Implementacion Variante#2
    public bool Existe()
    {
    //Lista de Categorias
    categorias = GetCategoryList();

    return (categorias.Where(itemCategoria =>
    (itemCategoria.Productos != null) && (itemCategoria.Productos.NombreProducto.Contains("Revista"))).Count() != 0);

    }

    //Implementacion Variante#3
    public bool Existe()
    {
    //Lista de Categorias
    categorias = GetCategoryList();

    return (categorias.Any(itemCategoria =>
    (itemCategoria.Productos != null) && (itemCategoria.Productos.NombreProducto.Contains("Revista"))));

    }

    Quiero que vean que si analizamos la variante 1 y 2 recorren el listado completo de Productos y luego con la funcion Count aplicado al listado resultado es que podemos devolver: verdadero o falso. Sin embargo, usando el Any tan solo al encontrar el primero que cumpla la condicion devolveriamos: verdadero.
    Espero, siempre que tengan un caso como este pues usen la funcion Any...
    Mis saludos,
    drachelita

    ResponderEliminar
  3. Este comentario ha sido eliminado por el autor.

    ResponderEliminar
  4. Hola, les muestro el uso de la función Select para un listado de elementos:

    private static string ConvertArrayToString(string[] lista,string separador)
    {
    return string.Concat(lista.Select(item => item + separador).ToArray());
    }

    Esta función que he implementado devuelve una cadena formada por los elementos de una lista unidos por un separador.
    Les mostrare un ejemplo en la que se puede utilizar:

    protected string DevuelveTitulo()
    {
    string[] listaCategorias = new string[3] { "ADM", "INF", "TEL" };
    string categorias = ConvertArrayToString(listaCategorias, ",");
    return "Categorías en exposición: " + categorias.Substring(0, categorias.Length - 1);
    }

    ResponderEliminar
  5. ayuda con sta consulta en sql y no se como pasarl a linq :
    SELECT LocalidadId, COUNT(*)[Productores] , SUM (FertilizanteId)[PaqEntregados] , SUM (BultosSA)[TotalBultosSA] , SUM (BultosDAP)[TotalBultosDAP] , SUM (BultosSA*0.05)[TotalTonSA] , SUM (BultosDAP*0.05)[TotalTonDAP] , SUM (BultosBIO)[TotalBIOPaq]
    FROM Productores
    WHERE LocalidadId = LocalidadId
    Group By LocalidadId

    ResponderEliminar