【问题标题】:How can I group by a list of elements?如何按元素列表分组?
【发布时间】:2012-05-01 23:09:30
【问题描述】:

我一次又一次地遇到这个问题:如何通过包含其他对象的列表对对象列表进行分组?

我有一个A 类型的对象列表,每个对象都有一个属性(我们称之为ListProp),它也是一个列表。 ListProp 具有 B 类型的元素。在ListProp 中有多个A 类型的元素与B-objects 相同,但ListProp 属性引用因元素而异。如何以最快的方式对这些 A-objects 进行分组,ListProp 中的 B-objects 相同?

示例代码:

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; }
}

【问题讨论】:

  • 为什么最后一个应该在第三组?它应该放在第一位,不是吗?
  • 因为元素的数量也应该相同。 0,1 != 0,1,1
  • 好吧,这是不正确的编辑。立即清除。

标签: c# .net linq group-by


【解决方案1】:

您可以实现IEqualityComparer&lt;List&lt;B&gt;&gt; 并在其他 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();
    }
}

然后你可以使用重载

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

【讨论】:

  • 完美!非常感谢 :) 为什么我总是忘记这些事情:SequenceEquals 和使用自定义比较器。你拯救了我的一天,尤其是我的周末;)
【解决方案2】:

试试这个:

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

它将相应地按0,1; 0,1; 0,1; 0,1,1; 0,1分组。

【讨论】:

  • 感谢您的想法,但这仅适用于示例。我的对象要复杂得多,因此很难以这种方式进行。但对于一个简单的方法,这是一个好主意。
  • @germanSharper:你知道这听起来像是从一个对象计算一个哈希码(目标也是你的:按标准相等的对象应该返回一个相等的值/哈希码)。它可以是一个列表或类。在我们的框架中,常用的方法是划定有意义的属性:“A:B:C:D:”。
  • @germanSharper:您还可以比较两种解决方案:在自定义比较器中分隔/连接项目。对我有意义
【解决方案3】:

我会通过以下方式解决这个问题:

  1. 将每个子元素(在 ListProp 属性中)与其父元素相关联
  2. 按孩子分组父母
  3. 投影结果

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()});

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-04-06
    • 2021-11-26
    • 2020-07-19
    • 1970-01-01
    • 2013-06-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多