【问题标题】:How to dynamically GroupBy using Linq如何使用 Linq 动态 GroupBy
【发布时间】:2016-12-11 00:51:55
【问题描述】:

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

好的,想象一下我有以下数据结构(对于这个 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"}},
       };

对于属性“数据”,用户将告诉我 GroupBy 的列序号;他们可能会说“不要按任何东西分组”,或者他们可能会说“按列[1]分组”或“按列[0] 按列[1]分组”。

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

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

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

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

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

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

谢谢

格里夫

【问题讨论】:

标签: c# linq lambda group-by


【解决方案1】:

您可以使用递归函数来创建动态 lambdaExpression。但您必须在函数中定义列 HardCode。

【讨论】:

    【解决方案2】:

    有了Row 数据结构,您所要求的就相对容易了。

    首先实现自定义IEqualityComparer&lt;IEnumerable&lt;string&gt;&gt;

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

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

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

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

    【讨论】:

      猜你喜欢
      • 2013-12-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-04-25
      • 1970-01-01
      • 1970-01-01
      • 2011-03-26
      相关资源
      最近更新 更多