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();