如何使用Linq动态GroupBy

时间:2016-08-04 18:44:17

标签: c# linq lambda group-by

有几个类似的发声帖,但没有一个完全符合我的要求。

好的,想象一下,我有以下数据结构(简化了这个LinqPad示例)

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

对于属性&#34; Data&#34;,用户将告诉我GroupBy的哪些列序号;他们可能会说&#34;不要按任何事情进行分组,或者他们可能会说#34;按专栏分组[1]&#34;或者&#34;按列[0] 列[1]和#34;分组。

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

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

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

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

但是,列数是可变的(零 - >多个);数据可能包含数百列,用户可能希望GroupBy数十列。

所以问题是,如何在运行时(动态)创建这个GroupBy?

由于

格里夫

2 个答案:

答案 0 :(得分:2)

使用Row数据结构,您要求的相对简单。

首先实施自定义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);
    }
}

现在你可以有这样的方法:

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参数指定的选定行值,这就是我们需要自定义相等比较器的原因(否则它们将通过引用进行比较) ,这不会产生所需的行为。)

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

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

传递null或空columnIndexes将有效地产生单个群组,即无群组。

答案 1 :(得分:0)

你可以使用Recursive函数来创建动态lambdaExpression。但是你必须在函数中定义HardCode列。