Select 列动态地来自 Datatable C#
Select columns dynamically from Datatable C#
我有一个 Datatable
如下所示
column1
Column2
Column3
Column 4
11
10
20
3
35
45
5
10
11
10
30
5
30
13
15
1
数据表大约有 30 列。样本我提到了 4 列(如上)
var groupbyColumns=list<string>(){"column1","column2"};
var aggregateColumns=List<string>(){"Column3"};
注意:列将根据用户在 UI 中的选择动态更改。
预期结果:
Column1
Column2
Column3
11
10
50
35
45
5
30
13
15
我必须根据 groupbyColumns
列表进行分组,然后根据 aggregateColumn
列表对列求和。
例如,如果 groupbyColumns
包含 2 列并且 aggregateColumn
包含 2 列意味着结果必须是 4 列。
有人可以帮我实现这个吗?
对提供序列相等性的 IEnumerable
使用 IEqualityComparer
,您可以生成可用于 GroupBy
的密钥。我使用静态方法来简化它的创建。
public static class Make {
public static IEqualityComparer<IEnumerable<T>> IESequenceEqual<T>() => new IEnumerableSequenceEqualityComparer<T>();
public static IEqualityComparer<IEnumerable<T>> IESequenceEqual<T>(T _) => new IEnumerableSequenceEqualityComparer<T>();
private class IEnumerableSequenceEqualityComparer<T> : IEqualityComparer<IEnumerable<T>> {
public bool Equals(IEnumerable<T> x, IEnumerable<T> y) =>
Object.ReferenceEquals(x, y) || (x != null && y != null && (x.SequenceEqual(y)));
public int GetHashCode(IEnumerable<T> items) {
// Will not throw an OverflowException
//unchecked {
// return items.Where(e => e != null).Select(e => e.GetHashCode()).Aggregate(17, (a, b) => 23 * a + b);
//}
var hc = new HashCode();
foreach (var item in items)
hc.Add(item);
return hc.ToHashCode();
}
}
}
由于您似乎指定答案应该是 DataTable
,因此您需要一种扩展方法来将结果转换为一个。我使用 IDictionary<string,object>
作为中介,在动态生成时传递列名和值。
public static class IEnumerableExt {
public static DataTable ToDataTable(this IEnumerable<IDictionary<string, object>> rows) {
var dt = new DataTable();
if (rows.Any()) {
foreach (var kv in rows.First().OrderBy(e => e.Key))
dt.Columns.Add(kv.Key, kv.Value.GetType());
foreach (var r in rows)
dt.Rows.Add(r.Values.Select(v => (object)v).ToArray());
}
return dt;
}
}
现在您只需按 groupByColumns
分组,对 aggregateColumns
求和并将结果转换为 DataTable
。我用ValueTuple
来表示每个列名,值对转换成Dictionary
.
var groupByColumns = new[] { "Column1", "Column2" }.ToList();
var aggregateColumns = new[] { "Column3" }.ToList();
var ans = src.AsEnumerable()
.GroupBy(r => groupByColumns.Select(c => r[c]), Make.IESequenceEqual<object>())
.Select(rg => groupByColumns.Zip(rg.Key, (c,v) => (c, v))
.Concat(aggregateColumns.Select(c => (c, v:(object)rg.Sum(r => Convert.ToDouble(r[c])))))
.ToDictionary(ck => ck.c, ck => ck.v))
.ToDataTable();
我有一个 Datatable
如下所示
column1 | Column2 | Column3 | Column 4 |
---|---|---|---|
11 | 10 | 20 | 3 |
35 | 45 | 5 | 10 |
11 | 10 | 30 | 5 |
30 | 13 | 15 | 1 |
数据表大约有 30 列。样本我提到了 4 列(如上)
var groupbyColumns=list<string>(){"column1","column2"};
var aggregateColumns=List<string>(){"Column3"};
注意:列将根据用户在 UI 中的选择动态更改。
预期结果:
Column1 | Column2 | Column3 |
---|---|---|
11 | 10 | 50 |
35 | 45 | 5 |
30 | 13 | 15 |
我必须根据 groupbyColumns
列表进行分组,然后根据 aggregateColumn
列表对列求和。
例如,如果 groupbyColumns
包含 2 列并且 aggregateColumn
包含 2 列意味着结果必须是 4 列。
有人可以帮我实现这个吗?
对提供序列相等性的 IEnumerable
使用 IEqualityComparer
,您可以生成可用于 GroupBy
的密钥。我使用静态方法来简化它的创建。
public static class Make {
public static IEqualityComparer<IEnumerable<T>> IESequenceEqual<T>() => new IEnumerableSequenceEqualityComparer<T>();
public static IEqualityComparer<IEnumerable<T>> IESequenceEqual<T>(T _) => new IEnumerableSequenceEqualityComparer<T>();
private class IEnumerableSequenceEqualityComparer<T> : IEqualityComparer<IEnumerable<T>> {
public bool Equals(IEnumerable<T> x, IEnumerable<T> y) =>
Object.ReferenceEquals(x, y) || (x != null && y != null && (x.SequenceEqual(y)));
public int GetHashCode(IEnumerable<T> items) {
// Will not throw an OverflowException
//unchecked {
// return items.Where(e => e != null).Select(e => e.GetHashCode()).Aggregate(17, (a, b) => 23 * a + b);
//}
var hc = new HashCode();
foreach (var item in items)
hc.Add(item);
return hc.ToHashCode();
}
}
}
由于您似乎指定答案应该是 DataTable
,因此您需要一种扩展方法来将结果转换为一个。我使用 IDictionary<string,object>
作为中介,在动态生成时传递列名和值。
public static class IEnumerableExt {
public static DataTable ToDataTable(this IEnumerable<IDictionary<string, object>> rows) {
var dt = new DataTable();
if (rows.Any()) {
foreach (var kv in rows.First().OrderBy(e => e.Key))
dt.Columns.Add(kv.Key, kv.Value.GetType());
foreach (var r in rows)
dt.Rows.Add(r.Values.Select(v => (object)v).ToArray());
}
return dt;
}
}
现在您只需按 groupByColumns
分组,对 aggregateColumns
求和并将结果转换为 DataTable
。我用ValueTuple
来表示每个列名,值对转换成Dictionary
.
var groupByColumns = new[] { "Column1", "Column2" }.ToList();
var aggregateColumns = new[] { "Column3" }.ToList();
var ans = src.AsEnumerable()
.GroupBy(r => groupByColumns.Select(c => r[c]), Make.IESequenceEqual<object>())
.Select(rg => groupByColumns.Zip(rg.Key, (c,v) => (c, v))
.Concat(aggregateColumns.Select(c => (c, v:(object)rg.Sum(r => Convert.ToDouble(r[c])))))
.ToDictionary(ck => ck.c, ck => ck.v))
.ToDataTable();