如何使用Linq

本文关键字:Linq 何使用 | 更新日期: 2023-09-27 18:00:58

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

好吧,假设我有以下数据结构(对于这个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"}},
       };

对于属性"Data",用户会告诉我要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数十列。

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

感谢

Griff

如何使用Linq

有了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将有效地产生单个组,即不分组。

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