如何使用Linq动态分组 [英] How to dynamically GroupBy using 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屋!