[Linq] Eficiencia Count() vs. Any()

El tema de la eficiencia se está volviendo importante, siempre lo ha sido, pero ahora más que nunca dado que los Frameworks nos brindan tantas abstracciones que podemos terminar haciendo lo mismo de una u otra forma sin preocuparnos por el que hace de fondo determinada función, y ese es el caso de estas dos sentencias. Dada una lista de la forma:

            List<string> palabras = new List<string>();
            palabras.Add("Primera");
            palabras.Add("Segunda");
            palabras.Add("Tercera");

Podemos entonces comprobar la existencia de elementos de las dos siguiente maneras:

            bool contieneElementosCount = palabras.Count() > 0;
            bool contieneElementosAny = palabras.Any();

Ambas nos devolverán true o false según corresponda, pero lo harán de forma diferente, y aquí es donde debemos empezar a evaluar que no conviene más, por un lado tenemos la función Count() que si miramos la documentación oficial de MSDN veremos que dice claramente, “Si el tipo de source implementa ICollection<T>, esa implementación se utiliza para obtener el recuento de elementos. De lo contrario, este método determina el recuento.” Pero ¿qué quiere decir con esto? Pues simple, si vemos la interfaz ICollection podemos ver que una de sus propiedades es Count, es decir que acudir este método intentara hacer algo como lo siguiente:

            ICollection<string> collection = palabras as ICollection<string>;
            int numeroElementos = collection.Count;

Cosa que para una colección que implementa de ICollection como lo hace List<T> no tiene ningún inconveniente, y como es el caso de todas las colecciones del espacio de nombres de System.Collection.Generic
pues todas implementan de la interfaz genérica ICollection. La excepción no sabría con que colección se presentaría y por lo tanto me parece la más rápida pues solo se debe hacer un casteo [de hecho ni se hace] para obtener el valor de count. El problema radica en que nos arriesgamos a tener una colección que no implemente de este y deba hacer el conteo manual [dentro de un while] y además de preguntar del lado de la linq por n > 0, haces un paso más y te arriesgas el doble.

Por el otro lado tenemos a Any(), este método determina si una secuencia contiene elementos. [*tomado de MSDN] de una forma un poco más directa, acudiendo directamente al método MoveNext de la enumeración pero sin tener que iterar sobre todos ellos, es decir, hará algo como lo siguiente:

            bool contieneElementos;
            using (IEnumerator<string> enumerator = palabras.GetEnumerator())
            {
                contieneElementos = enumerator.MoveNext();
            }

Entonces como podemos ver Any() es un método especialmente diseñado para este propósito y no se debe confundir con una “variación” de Count(), pues su propósito no es ese, si no el de determinar la cantidad de elementos de una colección.

Espero les sea de utilidad.

Hasta el próximo post.

Anuncios
[Linq] Eficiencia Count() vs. Any()

[Linq] Aplicar varios Order en una consulta

Hola, en los foros siempre se ve la pregunta de por qué al aplicar dos OrderBy seguidos no se obtiene el resultado esperado. Bien en este post mostrare la forma de hacerlo.
Teniendo el siguiente código:

    public class Producto
    {
        public string Nombre { getset; }

        public int Precio { getset; }
    }    
    class Program     {         static void Main(string[] args)         {             List<Producto> productos = new List<Producto>                                            {                                                new Producto {Nombre = "Computadora", Precio = 200},                                                new Producto {Nombre = "Televisor", Precio = 150},                                                new Producto {Nombre = "Impresora", Precio = 350}                                            };             List<Producto> prdsOrdenado = productos.OrderBy(x => x.Nombre).OrderBy(y => y.Precio).ToList();                     }     }

Obtendremos el siguiente resultado

Como se puede ver, el segundo OrderBy sobrescribe al primero ignorando el orden que este ya había aplicado, para lograr el resultado esperado, acudimos al método ThenBy.

El código quedaría de la forma:

    class Program
    { 
        static void Main(string[] args) 
        { 
            List<Producto> productos = new List<Producto> 
                                           { 
                                               new Producto {Nombre = "Computadora", Precio = 200}, 
                                               new Producto {Nombre = "Televisor", Precio = 150}, 
                                               new Producto {Nombre = "Impresora", Precio = 350} 
                                           }; 
            List<Producto> prdsOrdenado = productos.OrderBy(x => x.Nombre).ThenBy(y => y.Precio).ToList();             
        } 
    } 

Y obtendremos el siguiente resultado

NOTA: Si deseas usar el orden descendente deberás acudir al método ThenByDescending

Espero les sea de utilidad.

Hasta el próximo post.

[Linq] Aplicar varios Order en una consulta

[Linq] Tomar e ignorar valores de un origen de datos

En ocasiones necesitamos mostrar solo una parte del resultado de una consulta Linq, algo así como lo haríamos con un Top de SQL, bien, pues esta no es una limitante para Linq pues entre unos de los tantos y tan sobrecargados métodos de Enumerable tenemos esta funcionalidad.

El método Take nos permite obtener los primeros n resultados de una lista de valores, por ejemplo:

string[] paises = { "Colombia", "Bolivia", "Argentina", "Venezuela", "Perú" };
var primeros3paises = paises.Take(3);
Console.WriteLine(“El top tres de paises latinoamericanos”);
foreach (var pais in primeros3paises)
{
                Console.WriteLine(pais);
}

El resultado de esta consulta nos arrojará una lista con los tres primeros tres países de nuestro origen de datos.

Por si fuera poco Enumerable tiene un método que nos permite ignorar los primeros n elementos en una lista de datos, este método es Skip, veamos un ejemplo:

string[] paises = { "Colombia", "Bolivia", "Argentina", "Venezuela", "Peru" };
var  ultimos2paises = paises.Skip(3);
Console.WriteLine(“Los dos últimos países de la lista son: “);
foreach (var pais in ultimos2paises)
{
                Console.WriteLine(pais);
}

El resultado de esta consulta nos arrojara una lista con los dos últimos países de la lista.

Bien, pero si ahora no supiéramos el numero de valores que necesitaremos seleccionar/ignorar y que éste, por el contrario, dependiera de una expresión? por fortuna para nosotros Enumerable tiene la respuesta, en este encontramos los métodos TakeWhile y SkipWhile, veamos un ejemplo:

int[] numeros = { 0,1,4,5,2,6,3,9,7,8 };
var primerosMenoresque5 = numeros.TakeWhile(n => n < 5);
Console.WriteLine(“Primeros números menores que 5:”);
foreach (var num in primerosMenoresque5 ) 
{
        Console.WriteLine(num);
}

El método TakeWhile nos retornará una lista con los valores mientras la condición marcada se cumpla, es decir, recorrerá cada uno de los ítems de la colección comparándolo con la expresión indicada, en el momento que sea false dejará de compararse con los demás valores de la colección. Teniendo esto claro el resultado de esa expresión será una colección con los valores 0,1,4.

El método SkipWhile funciona de una manera similar, solo que ignorará los valores que cumplan con la condición indicada, mientras el resultado de la comparación sea true. Teniendo esto claro el resultado de hacer un SkipWhile(n=> n < 5) a la lista de valores del ejemplo anterior nos daría: 5, 2, 6, 3, 9, 7, 8.

Espero les haya quedado claro el uso de estos útiles métodos, sino no duden en escribir un comentario con su duda para que sea aclarada.

Hasta el próximo post.

[Linq] Tomar e ignorar valores de un origen de datos

Let de Linq – Declarando variables dentro de nuestra consulta

En ocasiones dentro de nuestras consultas Linq necesitamos almacenar el resultado de una operación matemática o la comparación de una cadena, etc. Para ello podemos usar la palabra clave let, ésta nos creará una nueva variable de rango dentro de la consulta y la inicializará con el resultado de la expresión que le asignemos.

Ejemplo, supongamos que recuperamos una lista de precios y tenemos un interés variable que aplicarle a estos y además el resultado de nuestra consulta deberá mostrar solo los precios que con dicho interés sean inferiores a $800, con linq esto es muy sencillo:

double[] precios = { 500, 300, 450, 250 };
double interes = 2.5;
var valores =
from prc in precios
 let precioInteres = prc * interes
 where precioInteres <= 800
 select new { prc, precioInteres };
 Console.WriteLine(“Totales inferiores a $800″);
 foreach (var valor in valores)
 {
                Console.WriteLine(“Precio sin interés: {0} – Con Interes: {1}”, valor.prc, valor.precioInteres);
}

Como podemos notar hemos declarado una variable de rango nueva con la que podemos trabajar y haciendo uso de los tipos anónimos creamos un objeto con las propiedades prc y precioInteres.

Sin embargo estas variables de rango declaradas con let tienen una insignificante limitación (comparada con lo útiles que son) y es que una vez después de inicializadas no se pueden reasignar, pero si podemos crear más variables de rango a partir de ellas.

Espero les sea de utilidad.

Hasta el próximo post.

Let de Linq – Declarando variables dentro de nuestra consulta

[Linq] Obtener los Radiobuttons a partir de su GroupName

Hoy en la mañana cuando llegue a la oficina y empecé a revisar los foros, me encontré con una pregunta curiosa (“Obtener todos los Radiobuttons a partir del GroupName“)… entonces pensé, – este hombre necesita saber el poder de Linq, así que le respondí de la siguiente forma.

En un escenario como el siguiente, una página web con seis Radiobuttons, separados en dos grupos de tres (“Grupo1” y “Grupo2”) y un botón que desencadena la acción.

Teniendo claro esto, manos al teclado Lo primero para poder trabajar con Linq (en este caso) es tener una colección IEnumerable del tipo RadioButton que contendrá todos los controles de este tipo, así:

IEnumerable&lt;RadioButton&gt; allRadios = Page.Form.Controls.OfType&lt;RadioButton&gt;();

Como vemos en la anterior línea de código accedemos a los controles del Form de la página y acudimos al método extensor OfType siendo T la clase por la que debemos “filtrar”. Como el extensor OfType retorna un IEnumarable (no un ControlCollection) ya tenemos acceso a todo el poder de Linq.

Ahora nos queda filtrar por el nombre de grupo, para esto he creado una función llamada getByGroupName, así:

private IEnumerable&lt;RadioButton&gt; getByGroupName(IEnumerable&lt;RadioButton&gt; allRadios, string groupName)
{
        return from radios in allRadios
               where radios.GroupName == groupName
               select radios;
}

De esta forma, haciendo uso de Linq dejamos de usar iteradores y hardcode pues le agregamos un tanto más de dinamismo a este tipo de trabajos.

Pero bueno ¿y si ahora queremos saber cuáles de este grupo están seleccionados o no? Fácil, la respuesta es Linq. Para esto creamos otro método al que llamaremos getAllCheckeds, así:

private IEnumerable&lt;RadioButton&gt; getAllCheckeds(IEnumerable&lt;RadioButton&gt; byGroupname, bool check)
 {
         return from radios in byGroupname
                where radios.Checked == check
                select radios;
 }

De esta forma podemos obtener toda la información que queramos de nuestros controles, haciendo uso de Linq. Finalmente, para el que se le dificulte el entendimiento del código, aquí lo dejo todo (más abajo el link de descarga):

protected void Button1_Click(object sender, EventArgs e)
{
        IEnumerable&lt;RadioButton&gt; allRadios = Page.Form.Controls.OfType&lt;RadioButton&gt;();
        IEnumerable&lt;RadioButton&gt; byGroupname = getByGroupName(allRadios, “Grupo1″);
        IEnumerable&lt;RadioButton&gt; radioCheked = getAllCheckeds(byGroupname, true);      
}

private IEnumerable&lt;RadioButton&gt; getByGroupName(IEnumerable&lt;RadioButton&gt; allRadios, string groupName)
{
        return from radios in allRadios
               where radios.GroupName == groupName
               select radios;
}
private IEnumerable&lt;RadioButton&gt; getAllCheckeds(IEnumerable&lt;RadioButton&gt; byGroupname, bool check)
{
        return from radios in byGroupname
               where radios.Checked == check
               select radios;
}

Descarga el ejemplo.

Espero les sea de utilidad.

Hasta el próximo post.

[Linq] Obtener los Radiobuttons a partir de su GroupName

C# y XML

En los foros siempre veo preguntas referentes al manejo de XML con C#, manejo que va desde su creación, consulta y edición, es por esto que me decidí a crear este documento en el que relato como trabajar XML con C#.

Para el manejo de XML, desde el Framework 3.5 se tiene un Namespace muy útil, el XML.Linq, éste será el que nos permitirá trabajar libremente con nuestros XML. Para empezar usaremos su clase “Principal” (según yo) la XDocument, esta nos permite: crear, leer, modificar (entre otras cosas) nuestros archivos. En fin, si deseas más documentación en la web puedes encontrar toda la que necesites para iniciar, por ahora empecemos a programar.

Para este ejemplo crearé una pequeña estructura de datos que contendrá información de los empleados de una compañía, la aplicación es de tipo WinForms.

Al proyecto de WinForm le agregamos un botón (bt_crear_XML) y llamara a nuestro método crear_XML, así:

private void bt_crear_XML_Click(object sender, EventArgs e)
{
            crear_XML();
}
private void crear_XML()
{
            XDocument miXML = new XDocument(new XDeclaration(&quot;1.0&quot;, &quot;utf-8&quot;, &quot;yes&quot;),
                new XElement(&quot;Empleados&quot;,

                new XElement(&quot;Empleado&quot;,
                new XAttribute(&quot;Id_Empleado&quot;, &quot;321654&quot;),
                new XElement(&quot;Nombre&quot;, &quot;Miguel Suarez&quot;),
                new XElement(&quot;Edad&quot;, &quot;30&quot;)),

                new XElement(&quot;Empleado&quot;,
                new XAttribute(&quot;Id_Empleado&quot;, &quot;123456&quot;),
                new XElement(&quot;Nombre&quot;, &quot;Maria Martinez&quot;),
                new XElement(&quot;Edad&quot;, &quot;27&quot;)),

                new XElement(&quot;Empleado&quot;,
                new XAttribute(&quot;Id_Empleado&quot;, &quot;987654&quot;),
                new XElement(&quot;Nombre&quot;, &quot;Juan Gonzales&quot;),
                new XElement(&quot;Edad&quot;, &quot;25&quot;))
                ));
            miXML.Save(@&quot;C:\Prueba\MiDoc.xml&quot;&quot;); //Debes tener permisos sobre la carpeta
            MessageBox.Show(&quot;Se ha creado un XML&quot;);
}

NOTA: No olvides agregar el namespace Xml.Linq (using System.Xml.Linq;)

Si ejecutas y pulsas el botón, vuala, habrás creado un XML. Como ves es bastante fácil, haciendo uso de las clases que nos proporciona este namespace. Perooo, ¿y ya? hasta aquí este post sería muy corto, así que me adelantaré y expondré un poco de Linq to Xml.

Consultar un Dato especifico

Para consultar un dato especifico, creare la función “buscarEnXml” que esperara un parámetro del tipo string (el Id_Empleado), la función queda así:

private void buscarEnXML(string idempleado)
{
            XDocument miXML = XDocument.Load(@&quot;C:\Prueba\MiDoc.xml&quot;); //Cargar el documento

            var nombreusu = from nombre in miXML.Elements(&quot;Empleados&quot;).Elements(&quot;Empleado&quot;)
                            where nombre.Attribute(&quot;Id_Empleado&quot;).Value == idempleado //Consultamos por el atributo
                            select nombre.Element(&quot;Nombre&quot;).Value; //Seleccionamos el nombre

            foreach (string minom in nombreusu)
            {
                MessageBox.Show(minom); //Mostramos un mensaje con el nombre del empleado que corresponde
            }
}

NOTA: para poder realizar consultas Linq, debes importar el namespace Linq (using System.Linq)

Cómo agregar un nuevo nodo.

Para agregar un nuevo nodo se usa la propiedad Root del XDocument, ésta propiedad obtiene la raiz del documento, dándonos la posibilidad de agregar los elementos y atributos de manera muy fácil, para esto, he creado un método addNode, que espera los parámetros id_Empleado, nombre y edad; todos del tipo string, el código queda así:

private void addNode(string idEmpleado, string nombre, string edad)
{
            XDocument miXML = XDocument.Load(@&quot;C:\Prueba\MiDoc.xml&quot;); //Cargamos
            miXML.Root.Add(   //Obtiene la raiz del documento (Empleados)                         
                new XElement(&quot;Empleado&quot;,
                    new XAttribute(&quot;Id_Empleado&quot;, idEmpleado),
                    new XElement(&quot;Nombre&quot;, nombre),
                    new XElement(&quot;Edad&quot;, edad))
                    );
            miXML.Save(@&quot;C:\Prueba\MiDoc.xml&quot;);
}

Cómo eliminar un nodo

Para eliminar un nodo se debe consultar si este existe, luego si se dispone a eliminar elemento a elemento con un foreach, esta es la forma en que yo lo hago, no se si exista una mas optima, (si conoces alguna te agradecería me lo hicieras saber). Creé un método dropNode, que espera el parámetro IdEmpleado del tipo string, el método queda así:

private void dropNode(string idEmpleado)
{
            XDocument miXML = XDocument.Load(@&quot;C:\Prueba\MiDoc.xml&quot;); //Cargar el archivo           

            var consul = from persona in miXML.Elements(&quot;Empleados&quot;).Elements(&quot;Empleado&quot;)
                         where persona.Attribute(&quot;Id_Empleado&quot;).Value == idEmpleado
                         select persona;
            consul.ToList().ForEach(x =&gt; x.Remove()); //Remover elemento a elemento.
            miXML.Save(@&quot;C:\Prueba\MiDoc.xml&quot;);            
}

Con este ultimo método hemos acabado con las operaciones básicas de acceso a datos, espero les halla sido de utilidad.

Descarga el ejemplo

Hasta el próximo post.

C# y XML