2012-05-30 28 views
5

Digamos que tengo un objeto que contiene el nombre de una persona y su ciudad de origen.Encontrar combinaciones de una lista agrupada usando LINQ en C#

public class personDetails 
{ 
    public string City; 
    public string Name; 
} 

Y tengo una lista con las siguientes entradas agregadas.

Name City 
John | London 
Jane | London 
Tom | New York 
Bob | New York 
Fred | New York 

Lo que estoy buscando son todas las posibles combinaciones de nombres, agrupadas por ciudad.

John Tom 
John Bob 
John Fred 
Jane Tom 
Jane Bob 
Jane Fred 

puedo hacer esto si sé de antemano el número de grupos, utilizando el siguiente código

List<personDetails> personList = new List<personDetails>(); 
//populate list 

var groupedPersons = personList.GroupBy(c => c.City); 
foreach (var item1 in groupedPersons[0]) 
{ 
    foreach (var item2 in groupedPersons[1]) 
    { 
     Console.WriteLine(item1.Name + " " + item2.Name); 
    }   
} 

Sin embargo, esto sólo funciona si sé que el número de grupos de antemano, y rápidamente se vuelve difícil de manejar a medida que aumenta la cantidad de grupos. Estoy seguro de que hay una forma elegante de hacerlo con LINQ. ¿Alguien puede arrojar algo de luz?

+0

Tome un vistazo a esta respuesta http://stackoverflow.com/questions/9168269/permutation-algorithms-in-c-sharp. Podrías unirte a tu lista consigo mismo. – Brad

+0

@Brad Eso funcionaría para el ejemplo de la lista con 2 ciudades. Lo que OP quiere es un producto cruzado N dimensional, donde N no se conoce hasta el tiempo de ejecución. Ese fragmento de código no lo proporciona. – Servy

Respuesta

3

Comenzaremos con el siguiente fragmento de código tomado verbatum de here. (Es un buen enlace, vale la pena leerlo).

public static class MyExtensions 
{ 
    public static IEnumerable<IEnumerable<T>> CartesianProduct<T>(this IEnumerable<IEnumerable<T>> sequences) 
    { 
     IEnumerable<IEnumerable<T>> emptyProduct = new[] { Enumerable.Empty<T>() }; 
     return sequences.Aggregate(
      emptyProduct, 
      (accumulator, sequence) => 
      from accseq in accumulator 
      from item in sequence 
      select accseq.Concat(new[] { item })); 
    } 
} 

Después de que todo lo que tenemos que hacer es:

var groupedPersons = personList.GroupBy(c => c.City) 
    //need an enumerable of enumerables, not an enumerable of groupings, 
    //because the method isn't covariant. 
    .Select(group => group.AsEnumerable()); 

var results = groupedPersons.CartesianProduct(); 
foreach (var group in results) 
{ 
    foreach (var person in group) 
    { 
     Console.Write(person.Name + " "); 
    } 
    System.Console.WriteLine(); 
} 
+0

De acuerdo, lo probé después de ingresar los datos que proporcionó e imprime los resultados esperados. – Servy

+0

Fantástico, funciona genial. Y un enlace muy interesante para arrancar, muchas gracias. – John

Cuestiones relacionadas