You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C# 无需自定义类使用LINQ GroupBy处理CSV调整客户消费数据

CSV消费金额按用户限额调整实现方案

需求说明

原始数据格式

NameSpendingCategory
John100Grocery
John50Dining
Michael55Carpooling
Steven15Grocery
Steven50Dining

预期输出格式

NameSpendingCategory
John40Grocery
John20Dining
Michael55Carpooling
Steven13.846Grocery
Steven46.154Dining

处理规则

  • 按姓名(Name)分组统计单个用户的总消费金额
  • 若用户总消费金额超过60,总金额上限设为60,Carpooling等除外类别的消费不受限额影响,金额直接保留
  • 普通类别消费的调整后金额,按原始金额占该用户普通类别总消费的比例重新分配
  • 调整后的数据保留原始行顺序,替换原Spending列后写入新CSV

现有代码问题

原有代码核心问题是把每行的三个属性拆分到三个独立的List,无法关联同一行的属性进行分组计算,同时缺少类别排除、比例分配、结果写回的逻辑。

修正后完整代码

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

namespace Query_csv
{
    // 定义实体存储每行原始数据和原始索引,保证输出顺序不变
    public class CustomerSpendingEntry
    {
        public string Name { get; set; }
        public double OriginalAmount { get; set; }
        public double RevisedAmount { get; set; }
        public string Category { get; set; }
        public int OriginalIndex { get; set; }
    }

    class Program
    {
        static void Main(string[] args)
        {
            string inputPath = @"Customer Spending.csv";
            string outputPath = @"Adjusted Customer Spending.csv";
            const double MAX_TOTAL_SPENDING = 60;
            // 除外类别集合,可随时新增其他不需要限额的类别
            var excludedCategories = new HashSet<string> { "Carpooling" };

            // 1. 读取并解析CSV
            var rawLines = File.ReadAllLines(inputPath);
            string header = rawLines[0];
            var entries = new List<CustomerSpendingEntry>();

            for (int i = 1; i < rawLines.Length; i++)
            {
                var parts = rawLines[i].Split(',');
                entries.Add(new CustomerSpendingEntry
                {
                    Name = parts[0].Trim(),
                    OriginalAmount = Convert.ToDouble(parts[1].Trim()),
                    Category = parts[2].Trim(),
                    OriginalIndex = i
                });
            }

            // 2. 按用户名分组处理
            var userGroups = entries.GroupBy(e => e.Name);
            foreach (var group in userGroups)
            {
                // 拆分除外类别和普通类别记录
                var excludedEntries = group.Where(e => excludedCategories.Contains(e.Category)).ToList();
                var normalEntries = group.Where(e => !excludedCategories.Contains(e.Category)).ToList();

                double excludedTotal = excludedEntries.Sum(e => e.OriginalAmount);
                double normalTotal = normalEntries.Sum(e => e.OriginalAmount);
                double totalSpending = excludedTotal + normalTotal;

                // 除外类别金额直接保留原值
                foreach (var entry in excludedEntries)
                {
                    entry.RevisedAmount = entry.OriginalAmount;
                }

                // 总金额未超过限额,普通类别也保留原值
                if (totalSpending <= MAX_TOTAL_SPENDING)
                {
                    foreach (var entry in normalEntries)
                    {
                        entry.RevisedAmount = entry.OriginalAmount;
                    }
                    continue;
                }

                // 总金额超限额,计算普通类别可分配总金额
                double availableNormalTotal = MAX_TOTAL_SPENDING - excludedTotal;
                if (availableNormalTotal <= 0 || normalTotal == 0)
                {
                    foreach (var entry in normalEntries)
                    {
                        entry.RevisedAmount = 0;
                    }
                    continue;
                }

                // 按比例分配金额
                foreach (var entry in normalEntries)
                {
                    double ratio = entry.OriginalAmount / normalTotal;
                    entry.RevisedAmount = Math.Round(ratio * availableNormalTotal, 3);
                }
            }

            // 3. 按原始索引排序,构建输出CSV
            var sortedEntries = entries.OrderBy(e => e.OriginalIndex).ToList();
            var outputLines = new List<string> { header };
            foreach (var entry in sortedEntries)
            {
                outputLines.Add($"{entry.Name},{entry.RevisedAmount},{entry.Category}");
            }

            File.WriteAllLines(outputPath, outputLines);
            Console.WriteLine("处理完成,结果已保存到 " + outputPath);
            Console.ReadKey();
        }
    }
}

代码说明

  • 自定义实体类存储每行全量信息和原始索引,避免输出行顺序错乱
  • 除外类别用HashSet存储,后续新增其他免限额类别直接加元素即可
  • 增加了除零、可分配金额为负的边界场景处理
  • 输出金额默认保留3位小数,可自行修改Math.Round的第二个参数调整精度

内容的提问来源于stack exchange,提问作者exd-AS

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.28 22:24:00