如何使用Linq动态分组 [英] How to dynamically GroupBy using Linq

查看:70
本文介绍了如何使用Linq动态分组的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有几篇类似的冠冕堂皇的帖子,但没有一篇完全符合我的要求.

There are several similar sounding posts, but none that do exactly what I want.

好的,假设我具有以下数据结构(此LinqPad示例已简化)

Okay, so imagine that I have the following data structure (simplified for this LinqPad example)

public class Row
{
    public List<string> Columns { get; set; }
}

public List<Row> Data
       => new List<Row>
       {
              new Row { Columns = new List<string>{ "A","C","Field3"}},
              new Row { Columns = new List<string>{ "A","D","Field3"}},
              new Row { Columns = new List<string>{ "A","C","Field3"}},
              new Row { Columns = new List<string>{ "B","D","Field3"}},
              new Row { Columns = new List<string>{ "B","C","Field3"}},
              new Row { Columns = new List<string>{ "B","D","Field3"}},
       };

对于属性"Data",用户将告诉我GroupBy的哪些列序号;他们可能会说什么都不要分组",或者说按列[1]分组"或按列[0]和列[1]分组".

For the property "Data", the user will tell me which column ordinals to GroupBy; they may say "don't group by anything", or they may say "group by Column[1]" or "group by Column[0] and Column[1]".

如果要按单个列分组,可以使用:

If I want to group by a single column, I can use:

var groups = Data.GroupBy(d => d.Columns[i]);

如果我想按2列分组,可以使用:

And if I want to group by 2 columns, I can use:

var groups = Data.GroupBy(d => new { A = d.Columns[i1], B = d.Columns[i2] });

但是,列数是可变的(零->多);数据可能包含数百列,而用户可能希望对数十列进行分组.

However, the number of columns is variable (zero -> many); Data could contain hundreds of columns and the user may want to GroupBy dozens of columns.

问题是,如何在运行时动态创建此GroupBy?

So the question is, how can I create this GroupBy at runtime (dynamically)?

谢谢

格里夫

推荐答案

有了这种 Row 数据结构,您所要求的相对容易.

With that Row data structure what are you asking for is relatively easy.

首先实现自定义 IEqualityComparer< IEnumerable< string>> :

public class ColumnEqualityComparer : EqualityComparer<IEnumerable<string>>
{
    public static readonly ColumnEqualityComparer Instance = new ColumnEqualityComparer();
    private ColumnEqualityComparer() { }
    public override int GetHashCode(IEnumerable<string> obj)
    {
        if (obj == null) return 0;
        // You can implement better hash function
        int hashCode = 0;
        foreach (var item in obj)
            hashCode ^= item != null ? item.GetHashCode() : 0;
        return hashCode;
    }
    public override bool Equals(IEnumerable<string> x, IEnumerable<string> y)
    {
        if (x == y) return true;
        if (x == null || y == null) return false;
        return x.SequenceEqual(y);
    }
}

现在您可以使用类似的方法:

Now you can have a method like this:

public IEnumerable<IGrouping<IEnumerable<string>, Row>> GroupData(IEnumerable<int> columnIndexes = null)
{
    if (columnIndexes == null) columnIndexes = Enumerable.Empty<int>();
    return Data.GroupBy(r => columnIndexes.Select(c => r.Columns[c]), ColumnEqualityComparer.Instance);
}

请注意,分组 Key 类型为 IEnumerable< string> ,并且包含由 columnIndexes 参数指定的选定行值,这就是为什么我们需要一个自定义的相等比较器(否则将通过引用比较它们,这不会产生必需的行为).

Note the grouping Key type is IEnumerable<string> and contains the selected row values specified by the columnIndexes parameter, that's why we needed a custom equality comparer (otherwise they will be compared by reference, which doesn't produce the required behavior).

例如,要按第0列和第2列分组,您可以使用以下内容:

For instance, to group by columns 0 and 2 you could use something like this:

var result = GroupData(new [] { 0, 2 });

传递 null 或空的 columnIndexes 将有效地产生单个组,即不进行分组.

Passing null or empty columnIndexes will effectively produce single group, i.e. no grouping.

这篇关于如何使用Linq动态分组的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆