7 trucos para simplificar sus programas con LINQ
Autor: Igor Ostrovsky Blogging
Traducción: Yuneisy Rodriguez


Desde que aprendí acerca de LINQ, me mantengo descubriendo nuevas formas de utilizarlo para mejorar mi código. Cada truco hace a mi código un poco más rápido de escribir y un poco más fácil de leer.

 

Este artículo resume algunos de los trucos que vine recopilando. Le mostraré cómo utilizar LINQ para:
Inicializar un arreglo.
Iterar a través de múltiples arreglos en un único ciclo.
Generar una secuencia aleatoria.
Generar una cadena.
Convertir secuencias o colecciones.
Convertir un valor a una secuencia de longitud 1.
Iterar sobre todos los subconjuntos de una secuencia.

 

1. Inicializar un arreglo

A menudo, usted necesita inicializar elementos de un arreglo tanto con un mismo valor, como con una secuencia de valores que se vayan incrementando, o posiblemente con una secuencia que se vaya incrementando o decrementando por un paso diferente de uno. Con LINQ, puede hacer todo esto dentro del inicializador de arreglo, no en ciclos necesariamente.
En el siguiente ejemplo de código, la primera línea, inicializa la variable a como un arreglo de longitud 10 con todos sus elementos valorizados con -1, la segunda línea inicializa b con (0,1..9), y la tercera línea inicializa c con (100,110,..., 190):

 

int[] a = Enumerable.Repeat(-1, 10).ToArray();

int[] b = Enumerable.Range(0, 10).ToArray();

int[] c = Enumerable.Range(0, 10).Select(i => 100 + 10 * i).ToArray();

 

Una palabra de advertencia: Si usted está inicializando arreglos grandes, puede querer renunciar a la elegancia y utilizar al viejo ciclo for en su lugar. La solución de LINQ aumentará el arreglo dinámicamente, por lo que los arreglos que se dejen de utilizar como parte de ese mismo proceso de crecimiento dinámico, serán recogidos por el mecanismo de garbage collection. Dicho esto, yo utilizo este truco todo el tiempo al inicializar arreglos pequeños, o en los códigos de prueba y depuración.

 

2. Iterar a través de múltiples arreglos en un único ciclo.

Un amigo me ha formulado una pregunta de C#: ¿Existe alguna manera de iterar varias colecciones con el mismo ciclo? Su código se veía así:

foreach (var x in array1) {
    DoSomething(x);
} 
foreach (var x in array2) {
    DoSomething(x);
}

En su caso, el cuerpo del ciclo era más largo y a él no le gustaba el código duplicado; pero tampoco deseaba crear un nuevo arreglo para almacenar los elementos de ambos arreglos array1 y array2.
LINQ ofrece una solución elegante para este problema: El operador Concat. Usted puede reescribir los dos ciclos anteriores, utilizando un único ciclo de la siguiente manera:

foreach (var x in array1.Concat(array2)) {
    DoSomething(x);
}

Note que desde que LINQ opera a nivel del enumerador, esto no va a crear un nuevo arreglo para almacenar los elementos del array 1 y array 2; por lo que, además de ser bastante elegante, esta solución es también eficiente en espacio.

 

3. Generar una secuencia aleatoria

Este es un truco sencillo para generar una secuencia aleatoria de longitud N:

Random rand = new Random();
var randomSeq = Enumerable.Repeat(0, N).Select(i => rand.Next());

Gracias a la naturaleza “perezosa” de LINQ, esta secuencia no es pre calculada y almacenada en un arreglo, sino que en su lugar los números aleatorios son generados bajo demanda, a medida que vamos iterando sobre randomSeq.

 

4. Generar una cadena.

LINQ también es una herramienta muy buena para generar varios tipos de cadenas. Esto me pareció muy útil para generar cadenas para propósitos de pruebas y depuración.

Supongamos que usted desea generar una cadena con un patrón de repetición "ABCABCABC…" de longitud N, utilizando LINQ, la solución es muy elegante:


string str = new string(
    Enumerable.Range(0, N)
    .Select(i => (char)(‘A’ + i % 3))
    .ToArray());

 

[EDIT] Petar Petrov sugirió otra forma interesante para generar cadenas utilizando LINQ. Su enfoque se puede aplicar a una mayor variedad de escenarios que mi solución anterior:

 

 string values = string.Join(string.Empty, Enumerable.Repeat(pattern, N).ToArray());

 

5. Convertir secuencias o colecciones.

Una cosa que no puede hacer en C# o Visual Basic es convertir una secuencia de tipo T a una secuencia de tipo U, incluso si T es una clase derivada de U. De aquí que usted no puede simplemente convertir una List<string> a List<object>. (Para una explicación del por qué, vea el post de Bick Byer)

 

Pero, si está tratando de convertir IEnumerable<T> a IEnumerable<U>, LINQ tiene una sencilla y eficiente solución para usted:

 

IEnumerable<string> strEnumerable = …;

IEnumerable<object> objEnumerable = strEnumerable.Cast<object>();

 

Si usted necesita convertir List<T> a List<U>, también hay una solución muy sencilla pero que involucra copiar la lista:

 

List<string> strList = …;

List<object> objList = new List<object>(strList.Cast<object>());

 

[EDIT] Chris Cavanagh sugirió una solución alternativa:

 

var objList = strList.Cast<object>().ToList();

 

6. Convertir un valor a una secuencia de longitud 1.

 

Cuando usted necesite convertir un valor a una secuencia de longitud 1, ¿Qué haría?
Usted podría construir un arreglo de longitud 1, pero yo prefiero el operador Repeat de LINQ.

 

IEnumerable<int> seq = Enumerable.Repeat(myValue, 1);

 

7. Iterar sobre todos los subconjuntos de una secuencia.

 

Algunas veces es útil iterar a través de todos los subconjuntos de un arreglo. Esta situación aparece más frecuentemente en soluciones de fuerza bruta para problemas difíciles. Para pequeñas entradas, suma de subconjuntos, boolean satisfiability, y el problema knapsack pueden ser resueltos fácilmente iterando a través de todos los subconjuntos de una determinada secuencia.
En LINQ podemos generar todos los subconjuntos de un arreglo de la siguiente manera:

 

T[] arr = …;

var subsets = from m in Enumerable.Range(0, 1 << arr.Length)

              select

                  from i in Enumerable.Range(0, arr.Length)

                  where (m & (1 << i)) != 0

                  select arr[i];

 

Note que si el número de subconjuntos sobrepasa un entero, el código anterior no va a funcionar; por lo que, solo utilícelo si usted sabe que la longitud de arr es cuando más 30.Si la longitud de arr es mayor que 30, es posible que usted no desee iterar a través de todos sus subconjuntos pues esto tomará minutos o más.

 

Comentarios y conclusiones.

 

Espero que usted encuentre estos trucos útiles y aplicables en sus programas.

 

Los códigos de ejemplo en este artículo están todos implementados en C#, pero pueden ser fácilmente adaptados a cualquier otro lenguaje .Net. Sin embargo, LINQ es más convenientemente usado desde los lenguajes .Net que soportan métodos extendidos, expresiones lambda e inferencia de tipos, como son C# y Visual Basic.

Hasta donde puedo asegurar, todos los códigos de este artículo funcionan, pero – como es común en la web – no garantizo nada. Como siempre, verifique cualquier código antes de usarlo.

 

Bibliografía