2011-11-15 27 views
7

¿Es posible agrupar en LINQ, utilizando una propiedad de colección?Colección LINQ GroupBy

p. Ej.

void Main() 
{ 
    var t1 = new Test() { Children = new List<string>() { "one", "two" } }; 
    var t2 = new Test() { Children = new List<string>() { "one", "two" } }; 
    var t3 = new Test() { Children = new List<string>() { "one", "three" }  }; 

    var tests = new List<Test>() { t1, t2, t3 }; 
    var anon = from t in tests 
       select new 
       { 
        Children = t.Children 
       }; 

    anon.GroupBy(t => t.Children).Dump(); 
} 

public class Test 
{ 
    public List<string> Children {get;set;} 
} 

En este ejemplo, yo esperaría para dos grupos:

Clave: Lista() { "uno", "dos"} Valor: t1, t2

Clave: Lista() {"one", "three"} Valor: t3

Tengo entendido que los tipos anónimos no se comparan por referencia, sino comparando la igualdad en sus propiedades públicas.

Sin embargo, el resultado real es tres grupos:

Clave: Lista() { "uno", "dos"} Valor: t1

Clave: Lista() { "uno", "dos"} Valor: t2

clave: Lista() { "uno", "tres"} Valor: t3

Si esto no es posible, ¿hay una manera de conseguir el resultado que quiero ?

suerte explicó esto claramente ...

Respuesta

0

El problema es que las listas no son exactamente idénticos. Está comparando igualdad para agrupar, y usted tiene dos nuevos List<string> s, que no son exactamente iguales. Puede, sin embargo, unirse a las cadenas de código hash, que producirían un resultado correcto:

tests.GroupBy(t => String.Join(string.Empty, t.Children.Select(c => c.GetHashCode().ToString()))); 
+0

Gracias - Entiendo eso (mi ejemplo de código se simplificó de mi problema real). Por favor, consulte mi código modificado muestra – TheNextman

+1

He actualizado mi respuesta para trabajar con su código existente. Por favor, échale un vistazo. Tampoco requiere un 'IEqualityComparer' personalizado. – doctorless

+0

Esto genera resultados muy extraños si el código hash es el mismo. – Caramiriel

4

Por defecto, GroupBy va a utilizar la igualdad de referencia cuando la agrupación por listas (que son los tipos de referencia).

ya que tienes nuevas instancias de la lista cada vez, no son iguales.

Sin embargo, hay una overload de GroupBy que le permite especificar una costumbre IEqualityComparer, de modo que usted puede implementar su propia manera de comparar una lista de cadenas, por ejemplo.

Para implementar esto, hay muchos other threads aquí acerca de la comparación de dos listas.

+0

Gracias - Lo entiendo (mi ejemplo de código se simplificó de mi problema real). Por favor vea mi ejemplo de código modificado – TheNextman

+1

Mi respuesta todavía se aplica, necesita crear un 'IEqualityComparer' personalizado que compare la igualdad de cada miembro en dos listas de cadenas. Luego, pase eso como el segundo parámetro a 'GroupBy'. – wsanville

+0

¿Consideraría un IEqualityComparer personalizado un poco demasiado para una agrupación LINQ única que probablemente no será necesaria dos veces? – doctorless

2

La razón por la que obtienes 3 grupos es porque List<T> implementa igualdad con referencia-igualdad predeterminada, no considerando la "igualdad de secuencia" de los elementos contenidos entre dos listas. Si desea dicha semántica, tendrá que implementar un IEqualityComparer<IList<T>> (o similar) e inyectarlo en la consulta GroupBy usando la sobrecarga que acepta un comparador de igualdad. Aquí hay una implementación de sample (para arreglos, no listas, pero fácilmente adaptable).

Si se siente cómodo con establecer la igualdad (orden y duplicados son irrelevantes), estás de suerte: se puede usar directamente HashSet<T> y la proporcionada CreateSetComparer método para la aplicación comparador:

var t1 = new Test { Children = new HashSet<string> { "one", "two" } }; 
    var t2 = new Test { Children = new HashSet<string> { "one", "two" } }; 
    var t3 = new Test { Children = new HashSet<string> { "one", "three" } }; 

    var tests = new List<Test> { t1, t2, t3 }; 

    // Only two groups: { one, two } and { one, three } 
    tests.GroupBy(t => t.Children, HashSet<string>.CreateSetComparer()) 
     .Dump();