首页 > 解决方案 > 在 C# 中使用 LINQ / GroupBy 来处理没有类的简单 csv

问题描述

在以这种格式处理之前,我有 csv 文件:

姓名 开支 类别
约翰 100 杂货店
约翰 50 用餐
迈克尔 55 拼车
史蒂文 15 杂货店
史蒂文 50 用餐

处理后,我想要这样的 csv 文件:

姓名 开支 类别
约翰 40 杂货店
约翰 20 用餐
迈克尔 55 拼车
史蒂文 13.864 杂货店
史蒂文 46.153 用餐

我需要根据每个客户的总支出金额来操作 csv 文件。

  1. 按相似的标题名称分组并找到总支出。
  2. 如果每个名称的总消费超过60,则将其重置为60,拼车除外(可以是多个案例)
  3. 按比例重新分配支出的子类别,例如,约翰在杂货店的支出是餐饮的 2 倍。因此,当我们重新分配时,Grocery 必须为 40,Dinning 必须为 20。

4.将新值保存在列表中,并在 csv 中重建原始“支出”列。

这是我的代码

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Query_csv
{
    class Program
    {
        static void Main(string[] args)
        {

            //Reading csv file
            string[] rawcsv = System.IO.File.ReadAllLines(@"Customer Spending.csv");

            //Empty array
            string[] customerSplitData;


            //Create an empty list for customerName,customerSpending,spendingCategory
            var customerName = new List<string>();
            var customerSpending = new List<double>();
            var spendingCategory = new List<string>();

            //Skip header and print all rows as you see in Notepad using for loop
            Console.WriteLine("Print each row on a new line:  ");
            for (int i = 1; i < rawcsv.Length; i++)
            {
                string lines = rawcsv[i];
                //Console.WriteLine(lines);

                customerSplitData = lines.Split(',');
                //Console.WriteLine(customerSplitData[0]);

                customerName.Add(customerSplitData[0]);

                double casted_spending = Convert.ToDouble(customerSplitData[1]);
                customerSpending.Add(casted_spending);


                spendingCategory.Add(customerSplitData[2]);
                
            }

            //Defining key
            var key = customerName;

            //Defining values
            var values = customerSpending;

            //For every key and values Using Query Syntax or GroupBY /LINQ
            var data = customerSplitData.GroupBy(entry => entry.key).ToDictionary(group => group.Key, group => group.Select(entry => entry.value));

            //Creating a List of new customerSpending to save the reproportioned spending values after Querying
            var revisedcustomerspending = new List<double>();

            //Iterating through all the keys and Verifying the total
            var iSuccess = data.All(entry => entry.Value.Sum() > 60);
            if (iSuccess )
                //For every customer reproportion its spending amount by its original spending proportion
                Console.WriteLine("The Total customer spending is set to 60 and the spending subcategories are reproportion as original");

            else
                //For every customer do not reproportion but take the spending amount as it is original 
                Console.WriteLine("The Total customer spending is taken as default i.e less than or equal to 60 and the subcategory spending is not reproportioned");
        }

        //Save the reproportion values in the new CustomerSpending
        
        //Rebuild the csv string and save it as CSV


        Console.ReadKey();
        }
    }
}

标签: c#

解决方案


我没有使用 csv 进行实验,而是创建了一个ValueTuple列表,如下所示:

var source = new List<(string Name, double Spending, string Category)>()
{
    ("John", 100, "Grocery"),
    ("John", 50, "Dining"),
    ("Michael", 55, "Carpooling"),
    ("Steven", 15, "Grocery"),
    ("Steven", 50, "Dining"),
};

使用 linq,您可以使用以下查询来解决它:

var target =
    from @record in source
    group @record by @record.Name into customerRecords
    let totalSpending = customerRecords.Sum(r => r.Spending)
    let ratio = totalSpending < 60 ? 1 : 60 / totalSpending
    from customer in customerRecords
    select (customer.Name, customer.Spending * ratio, customer.Category);
  • 首先让我们按名称对记录进行分组
  • 然后让我们计算总支出
  • 从中我们可以计算出比率
    • 如果总支出小于 60,则比率将为 1
  • 最后让我们遍历分组并发出一个具有调整值的新元组

让我们打印出来:

foreach (var item in target) Console.WriteLine(item);

那么这将是输出:

(John, 40, Grocery)
(John, 20, Dining)
(Michael, 55, Carpooling)
(Steven, 13.846153846153847, Grocery)
(Steven, 46.15384615384615, Dining)

更新转换为 csv

如果您不想为此使用 3rd 方库,则可以像这样转换它:

var csvBuilder = new StringBuilder();
csvBuilder.AppendLine("Name,Spending,Category");
foreach (var item in target)
{
    csvBuilder.AppendLine(string.Join(",", item.Name, item.Item2, item.Category));
}
File.WriteAllText("output.csv", csvBuilder.ToString());

如果您愿意学习 3rd 方库,那么我会推荐CsvHelper


推荐阅读