首页 > 解决方案 > 从 Datatable C# 中动态选择列

问题描述

我有一个Datatable喜欢下面

第 1 列 第 2 列 第 3 列 第 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 中的选择动态更改。

预期结果:

第 1 列 第 2 列 第 3 列
11 10 50
35 45 5
30 13 15

我必须根据groupbyColumns列表分组,然后根据列表对列进行求和aggregateColumn

例如,如果groupbyColumns包含 2 列并且aggregateColumn包含 2 列,则意味着结果必须为 4 列。

有人请帮我实现这一目标吗?

标签: c#.netlinqado.net

解决方案


使用提供序列相等性的IEqualityComparerfor IEnumerable,您可以生成可用于 的密钥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();
        }
    }
}

由于您似乎指定答案应该是 a 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();

推荐阅读