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