【问题标题】:How to add duplicates or merge entries in a complex list?如何在复杂列表中添加重复项或合并条目?
【发布时间】:2021-07-04 11:41:09
【问题描述】:

我有一个我认为很容易解决的问题,但经过几天的尝试失败后,我正在寻求帮助。

我在 Excel 中有一个如下所示的表格:

Transaction Date Bank Account Balance
44287 Account 1 500
44287 Account 1 -700
44287 Account 2 -200
44286 Account 1 300
44286 Account 3 -150

该表代表多个银行账户,我想获取所有账户每个日期的累积余额。例如,我期望的输出是:

Transaction Date Balance
44287 -900
44286 150

如果同一账户在给定日期有多个交易,例如在“44287”上,账户 1 的余额有两个值,所以我会采用最大绝对值 -700(并忽略 500)。为简单起见,我使用 Excel 中的默认日期数字表示法。

我已经用尽了 Excel 中的所有选项,所以我用 C# 创建了自己的简单控制台应用程序。

这是我目前所拥有的。

我正在将 Excel 中的输入读入var data = new List<List<string>>();。第一个外部列表存储列,第二个内部列表存储行。因此,例如 data[2][3] 将等于 -200

private static void Calculate(string filePath, string sheetName)
{
    var data = new List<List<string>>();

    using (ExcelReader excel = new ExcelReader(filePath))
    {
        excel.SetWorksheet(sheetName);
        data = excel.ReadUsedRangeToList();
    }

    var date = data[0];
    var account = data[1];
    var balance = data[2];

    // group transactions by date
    var groupedByDate = new List<List<List<string>>>();
    for (int r = 1; r < date.Count - 2; r++)
    {
        var lines = new List<List<string>>();
        for (int ri = r; ri < date.Count; ri++)
        {
            if (date[r] == date[ri])
            {
                var temp = new List<string>();
                temp.Add(date[ri]);
                temp.Add(account[ri]);
                temp.Add(balance[ri]);

                lines.Add(temp);

                // need to set r to the latest ri to finish at the last line
                r = ri;
            }
            else
            {
                r = ri - 1;
                break;
            }
        }
        groupedByDate.Add(lines);
    }
}

我首先将我的原始列表分组到一个新列表中,该列表也按日期对数据进行分组。然而,这为列表添加了另一个深度级别。然后我尝试按银行帐户对其进行分组,但这会在列表中添加另一个级别,并且所有这些循环的逻辑变得令人兴奋。

我开始认为我采取了错误的方法来解决这个问题。我做了很多谷歌搜索,似乎使用 LINQ 而不是使用循环可能会更好?我以前使用过 LINQ,但只用于非常琐碎的事情。

如果有人能指出我正确的方向,那就太好了。我对所有想法持开放态度。如果有人知道您是否可以在我可能错过的 Excel 中轻松完成此操作,请告诉我!

【问题讨论】:

    标签: c# excel list merge duplicates


    【解决方案1】:

    在 Excel 中,您可以使用 Power Query 执行此操作。

    查看 cmets 并探索 Applied Steps 窗口以了解算法,我认为这是您想要的:

    您可能需要编辑数据类型以反映十进制数字和日期。

    M 码

    let
        Source = Excel.CurrentWorkbook(){[Name="Table3"]}[Content],
        #"Changed Type" = Table.TransformColumnTypes(Source,{
                {"Transaction Date", Int64.Type}, {"Bank Account", type text}, {"Balance", Int64.Type}}),
    
    //Group by Date and Account
    //Extract the amount corresponding to maximimum absolute value for each date/account grouping
        #"Grouped Rows" = Table.Group(#"Changed Type", {"Transaction Date", "Bank Account"}, {
            {"useBal", each List.Accumulate([Balance],0,(current,status)=> if Number.Abs(status) > Number.Abs(current)
                            then status else current)}
            }),
     
     //Then group by date and SUM the balances
        #"Grouped Rows1" = Table.Group(#"Grouped Rows", {"Transaction Date"}, 
            {{"Balance", each List.Sum([useBal]), type number}})
    in
        #"Grouped Rows1"
    

    注意

    M 代码修改为使用“正确”的数据类型

    let
        Source = Excel.CurrentWorkbook(){[Name="Table3"]}[Content],
        #"Changed Type" = Table.TransformColumnTypes(Source,{
                {"Transaction Date", type date}, {"Bank Account", type text}, {"Balance", Currency.Type}}),
    
    //Group by Date and Account
    //Extract the amount corresponding to maximimum absolute value for each date/account grouping
        #"Grouped Rows" = Table.Group(#"Changed Type", {"Transaction Date", "Bank Account"}, {
            {"useBal", each List.Accumulate([Balance],0,(current,status)=> if Number.Abs(status) > Number.Abs(current)
                            then status else current), Currency.Type}
            }),
     
     //Then group by date and SUM the balances
        #"Grouped Rows1" = Table.Group(#"Grouped Rows", {"Transaction Date"}, 
            {{"Balance", each List.Sum([useBal]),Currency.Type}})
    in
        #"Grouped Rows1"
    
    

    【讨论】:

    • 非常感谢!我不知道你可以在 Excel 中做到这一点。经过几个小时的尝试,我终于设法编写了一个适用于 C# 的解决方案。我想这并没有白费,因为我确实学到了一些新东西——我已将其附在另一条评论中以供参考。我也一定会尝试您的方法,因为它似乎是一种更有效的方法。
    • @TheEngineer 不客气。我编辑我的答案以使用我认为所需的数据类型显示修改。
    【解决方案2】:

    更新

    我已经设法使用 LINQ 解决了我的问题。我在 YouTube 上做了一个速成课程,这是主要的 video 帮助。我在下面发布我的解决方案以供将来参考。它可能不是最佳的,但它可以完成工作。

    public class Transaction
    {
        public float Date { get; set; }
        public string Account { get; set; }
        public float Value { get; set; }
    }
    
        private static void Calculate(string filePath, string sheetName)
        {
            // Get raw data from Excel
            var data = new List<List<string>>();
            using (ExcelReader excel = new ExcelReader(filePath))
            {
                excel.SetWorksheet(sheetName);
                data = excel.ReadUsedRangeToList();
            }
    
            // Organize the data using Transaction class
            var transactions = new List<Transaction>();
            for (int r = 1; r < data[0].Count; r++)
            {
                var transaction = new Transaction();
                transaction.Date = int.Parse(data[0][r]);
                transaction.Account = data[1][r];
                transaction.Value = float.Parse(data[2][r]);
    
                transactions.Add(transaction);
            }
    
            // Use LINQ 
            // https://www.youtube.com/watch?v=71medpGp1nc
    
            var flattened = new List<Transaction>();
            var groupedByDate = transactions.GroupBy(x => x.Date);
            foreach (var groupDate in groupedByDate)
            {
                Console.WriteLine($"Key: {groupDate.Key} | Count: {groupDate.Count()}");
                Console.WriteLine($"Key: {groupDate.Key} | Max: {groupDate.Max(x => x.Value)} | Min: {groupDate.Min(x => x.Value)} | Sum: {groupDate.Sum(x => x.Value)}");
    
                var groupedByAccount = groupDate.GroupBy(x => x.Account);
                foreach (var groupAccount in groupedByAccount)
                {
                    Console.WriteLine($"Key: {groupAccount.Key} | Count: {groupAccount.Count()}");
    
                    var max = groupAccount.Max(x => x.Value);
                    var min = groupAccount.Min(x => x.Value);
    
                    float extreme;
                    if (Math.Abs(max) > Math.Abs(min))
                    {
                        extreme = max;
                    }
                    else
                    {
                        extreme = min;
                    }
    
                    Console.WriteLine($"Key: {groupAccount.Key} | Max: {groupAccount.Max(x => x.Value)} | Min: {groupAccount.Min(x => x.Value)}");
                    Console.WriteLine($"Key: {groupAccount.Key} | Extreme: {extreme}");
    
                    var temp = new Transaction();
                    temp.Date = groupDate.ToList()[0].Date;
                    temp.Value = extreme;
                    flattened.Add(temp);
                }
                
                Console.WriteLine("----------");
            }
    
            // sum the value of all transactions with the same date
            var output = new List<Transaction>();
            var flattenedGroupedByDate = flattened.GroupBy(x => x.Date);
    
            foreach (var date in flattenedGroupedByDate)
            {
                var temp = new Transaction();
                temp.Value = date.Sum(x => x.Value);
                temp.Date = date.ToList()[0].Date;
    
                output.Add(temp);
            }
        }
    

    【讨论】:

      猜你喜欢
      • 2012-09-25
      • 2019-05-12
      • 2018-12-10
      • 2016-06-02
      • 1970-01-01
      • 2012-04-13
      • 2011-01-07
      • 2018-10-08
      • 1970-01-01
      相关资源
      最近更新 更多