2012-04-20 24 views
8

Me encuentro con este problema una y otra vez: ¿cómo puedo agrupar una lista de objetos por una lista que contiene otros objetos?¿Cómo puedo agrupar por una lista de elementos?

Tengo una lista de objetos del tipo A y cada uno de estos objetos tiene una propiedad (vamos a llamarlo ListProp) que es una lista también. ListProp tiene elementos del tipo B. Hay varios elementos del tipo A con idéntico B -objetos en ListProp, pero la referencia de propiedad ListProp difiere de un elemento a otro. ¿Cómo puedo agrupar estos objetos A de la manera más rápida, donde los objetos B en ListProp son idénticos?

Código de ejemplo:

class Program 
{ 
    static void Main(string[] args) 
    { 
     var exampleList = new List<A> 
     { 
      // Should be in first group 
      new A { ListProp = new List<B> 
      { 
       new B { Prop = new C { Number = 0 }}, 
       new B { Prop = new C { Number = 1 }} 
      }}, 
      // Should be in first group 
      new A { ListProp = new List<B> 
      { 
       new B { Prop = new C { Number = 0 }}, 
       new B { Prop = new C { Number = 1 }} 
      }}, 
      // Should be in second group 
      new A { ListProp = new List<B> 
      { 
       new B { Prop = new C { Number = 0 }}, 
       new B { Prop = new C { Number = 1 }}, 
       new B { Prop = new C { Number = 1 }} 
      }}, 
      // Should be in third group 
      new A { ListProp = new List<B> 
      { 
       new B { Prop = new C { Number = 0 }}, 
       new B { Prop = new C { Number = 0 }} 
      }} 
     }; 

     // Doesn't work because the reference of ListProp is always different 
     var groupedExampleList = exampleList.GroupBy(x => x.ListProp); 
    } 
} 

class C 
{ 
    public int Number { get; set; } 
    public override bool Equals(object o) 
    { 
     if (o is C) 
      return Number.Equals(((C)o).Number); 
     else 
      return false; 
    } 
} 

class B 
{ 
    public C Prop { get; set; } 
} 

class A 
{ 
    public IList<B> ListProp { get; set; } 
} 
+1

Por qué la última debería ser el tercer grupo? Debería estar en primer lugar, ¿no? – abatishchev

+0

Porque la cantidad de elementos debería ser la misma también. 0,1! = 0,1,1 – germanSharper

+0

Ok, eso fue una edición incorrecta. Despejado ahora. – abatishchev

Respuesta

6

Puede implementar IEqualityComparer<List<B>> y usarlo en la otra sobrecarga de GroupBy.

public class ListOfBEqualityComparer : IEqualityComparer<List<B>> 
{ 
    public bool Equals(List<B> x, List<B> y) 
    { 
     // you can also implement IEqualityComparer<B> and use the overload 
     return x.SequenceEqual(y); 
    } 

    public int GetHashCode(List<B> obj) 
    { 
     //implementation of List<T> may not work for your situation 
     return obj.GetHashCode(); 
    } 
} 

continuación, puede utilizar la sobrecarga de

var groupedExampleList = exampleList.GroupBy(x => x.ListProp, 
              new ListOfBEqualityComparer()); 
+0

¡Perfecto! Muchas gracias :) Por qué siempre me olvido de esto: SequenceEquals y el uso de un comparador personalizado. Me salvaste el día y especialmente mi fin de semana;) – germanSharper

+0

De nada :) –

4

Prueba esto:

GroupBy(x => String.Join(",", x.ListProp)); 

Se agrupará por 0,1; 0,1; 0,1; 0,1,1; 0,1 en consecuencia.

+0

gracias por su pensamiento, pero esto solo funciona para el ejemplo. Mis objetos son mucho más complejos, por lo que sería difícil hacerlo de esta manera. Pero para un enfoque simple, esta es una buena idea. – germanSharper

+0

@germanSharper: Sabes que esto suena como un cálculo de un código hash de un objeto (apunta igual que tú: igual por un criterio los objetos deberían devolver un valor igual/código hash). Podría ser una lista o clase. En nuestro marco, el enfoque común es delimitar las propiedades significativas: "A: B: C: D:". – abatishchev

+0

@germanSharper: También puede comparar las dos soluciones: delimitar/unir elementos dentro del comparador personalizado. Tiene sentido para mí – abatishchev

0

que se acercaría a esta de la siguiente manera:

  1. asociar cada elemento secundario (en la propiedad ListProp) con su padre
  2. Grupo los padres por los niños
  3. Proyecta los resultados

var data = exampleList.SelectMany(a=>a.ListProp.Select(x=>new{Key = x.Prop.Number, Value = a})) 
      .GroupBy(x=>x.Key) 
      .Select(g=>new {Number = g.Key, Items = g.ToList()}); 
Cuestiones relacionadas