2010-11-01 18 views
41

que tienen una estructura de clases de este modo:¿Hay una buena forma de LINQ para hacer un producto cartesiano?

Person 
Dogs (dog 1, dog 2, etc) 
Puppies (puppy A, puppy B, etc) 

Hay una persona. Él tiene 1..n perros. Cada perro tiene 1..n cachorros.

Quiero una lista de todas las posibles combinaciones de cachorros, tomando 1 cachorro de cada perro. Por ejemplo:

perro 1 Un perrito, perro 2 del perrito Un perro 1 Un perrito, perro 2 del perrito B perro 1 cachorro B, perro 2 del perrito Un perro 1 cachorro B, perro 2 del perrito B

Si lo fue en las tablas de SQL, que haría algo como lo siguiente a 'multiplicar' las tablas:

select * from puppies a, puppies b where a.parent='dog1' and b.parent='dog2' 

¿hay alguna manera de LINQ-ish para hacer esto un poco cosa ???

Gracias tanto

Respuesta

62

Si entiendo la pregunta, desea que el producto cartesiano de n conjuntos de cachorros.

Es fácil conseguir el producto cartesiano si sabe en tiempo de compilación cuántos juegos se encuentran:

from p1 in dog1.Puppies 
from p2 in dog2.Puppies 
from p3 in dog3.Puppies 
select new {p1, p2, p3}; 

dog1 Supongamos que tiene cachorros p11, p12, dog2 tiene p21 cachorro, y Dog3 tiene cachorros p31, p32. Esto le da

{p11, p21, p31}, 
{p11, p21, p32}, 
{p12, p21, p31}, 
{p12, p21, p32} 

Donde cada fila es de tipo anónimo. Si no sabe en tiempo de compilación cuántos conjuntos hay, puede hacerlo con un poco más de trabajo.Véase mi artículo sobre el tema:

http://ericlippert.com/2010/06/28/computing-a-cartesian-product-with-linq/

y esta cuestión StackOverflow:

Generating all Possible Combinations

Una vez que tenga el método CartesianProduct<T> entonces se puede decir

CartesianProduct(from dog in person.Dogs select dog.Puppies) 

para obtener

{p11, p21, p31}, 
{p11, p21, p32}, 
{p12, p21, p31}, 
{p12, p21, p32} 

Donde cada fila es una secuencia de cachorros.

¿Tiene sentido?

+0

¡Bingo! Gracias por la ayuda. – Chris

+0

Entonces, ¿tendría razón al decir que esta es una alternativa a la programación recursiva? –

+2

@Nick: creo que sería más adecuado decir que esta es una alternativa a la * imperativa * programación. El objetivo de LINQ es que diga lo que quiere, programa * declarativamente * y el compilador averigua cómo hacerlo en función de las bibliotecas de tiempo de ejecución disponibles. Si esas bibliotecas hacen su trabajo usando programación recursiva o programación iterativa, ese es su negocio. –

12

dogs.Join (cachorros,() => true,() => true, (uno, dos) => nueva tupla (uno, dos));

Puede hacer una unión normal, pero los selectores devuelven el mismo valor, porque quiero que todas las combinaciones sean válidas. Cuando combine, coloque ambos en una sola tupla (o una estructura de datos diferente de su elección).

leftSide.SelectMany((l) => rightSide, (l, r) => new Tuple(l, r)); 

Esto debería hacer un producto cartesiano.

+0

Muchas gracias. Wow, eso es complicado. Va a mostrar por qué inventaron la sintaxis de comprensión de consultas de linq, sin duda es más legible que el fluido en instancias como esta. – Chris

+0

@Eric Lippert no es el punto de SelectMany para contraer varios IEnumerable (desde varios objetos del mismo tipo) en un IEnumerable ? Después de revisar la documentación, no encuentro una forma diferente de traducir su producto N cartesiano en LINQ sin la sintaxis de la consulta. ¿No se define la unión como un conjunto limitado del producto cartesiano? ¿Me estoy perdiendo un operador? ¿La cláusula from es solo equivalente a una cláusula foreach, o hay un operador separado? – McKay

+2

Eso son muchas preguntas. (1) no es el objetivo de Colapso múltiple múltiple IEnumerable en un IEnumerable ? Sí, aunque por supuesto hace más que eso. Desde el punto de vista de la "secuencia", SelectMany es el operador del producto cartesiano con una proyección en la parte posterior. Desde un punto de vista de "mónada" más general, SelectMany es la operación de vinculación en el patrón de mónadas. –

9

Si desea que todas las combinaciones posibles de perro y del perrito, que haría una combinación cruzada:

from dog in Dogs 
from puppy in Puppies 
select new 
{ 
    Dog = dog, 
    Puppy = puppy 
} 
+0

En realidad quiero todas las combinaciones posibles de N series de cachorros, pero gracias por ponerme en el camino correcto. – Chris

Cuestiones relacionadas