首页 > 解决方案 > 如何使用 Linq 在某些操作中聚合两个 List?

问题描述

我已经定义了包含名称、主题属性的类 StudentData,这些属性是枚举类型,并且得分为双倍。一些数据填充在List结构StudentNameList和StudentDataList中。

public class StudentData
{
    public StudentName Name { get; set; }
    public Subject Subject { get; set; }
    public double Score { get; set; }

    public StudentData(StudentName name, Subject subject, double score)
    {
        Name = name;
        Subject = subject;
        Score = score;
    }
}

public enum StudentName
{
    Tom,
    Nikki,
    Benny
}

public enum Subject
{
    Math,
    Physic,
    History
}
    List<StudentName> studentName = new List<StudentName>();
    studentName.Add( StudentName.Benny);
    studentName.Add(StudentName.Nikki);
    studentName.Add(StudentName.Tom);

    List<StudentData> studentDataList = new List<StudentData>();

    studentDataList.Add(new StudentData(StudentName.Benny, Subject.History, 85));
    studentDataList.Add(new StudentData(StudentName.Benny, Subject.Math, 60));
    studentDataList.Add(new StudentData(StudentName.Benny, Subject.Physic, 75));

    studentDataList.Add(new StudentData(StudentName.Nikki, Subject.History, 65));
    studentDataList.Add(new StudentData(StudentName.Nikki, Subject.Math, 70));
    studentDataList.Add(new StudentData(StudentName.Nikki, Subject.Physic, 75));

    studentDataList.Add(new StudentData(StudentName.Tom, Subject.History, 88));
    studentDataList.Add(new StudentData(StudentName.Tom, Subject.Math, 90));
    studentDataList.Add(new StudentData(StudentName.Tom, Subject.Physic, 95));

下面定义了因子表

        List<double> FactorTable = new List<double>();
        FactorTable.Add(1.05);
        FactorTable.Add(1.02);

我想要实现的是创建一个 Dictionary<StudentName, Double> ,该值存储该学生的所有加权平均分数(将所有分数与因子列表相乘)。例如在本尼的案例中:

 Dictionary<StudentName, double> StudentDic = new Dictionary<StudentName, double>();

键:Benny,值:(85 * 1.05) + (85 *1.02) + (60 * 1.05) + (60 * 1.02) + (75 * 1.05) + (75 * 1.02)

我认为这可以通过几个聚合 Linq 操作来完成。但是,当我开始实施时,这并不容易实现。我所做的如下:

            foreach (var student in studentName)
        {
            var filterStudent = studentDataList.Where(s => s.Name == student);

            List<double> multipledResult = new List<double>();

            foreach (var s in filterStudent)
            {
                foreach (var f in FactorTable)
                {
                    multipledResult.Add(s.Score * f);
                }
            }
            StudentDic.Add(student, multipledResult.Average());
        }

通过使用 Linq 操作来简化算法,我想念什么。有人可以建议是否有办法改进它?

标签: c#linq

解决方案


这应该给你一个起点:

var result = 
    studentDataList
    .SelectMany(sdl => FactorTable.Select(ft => new {
        sdl.Name, 
        aggScore = ft * sdl.Score
    }))
    .GroupBy(prod => prod.Name)
    .Select(g => new { 
        Name = (StudentName)g.Key, 
        wtAvg = g.Sum(x => x.aggScore) / g.Sum(x => x.ft)
    })
    .ToDictionary(x => (StudentName)x.Name, x => x.wtAvg);

SelectMany -> Select方法通过每个权重为您提供每个分数的您想要的产品。然后链为每个人创建一个组。最终Select总结了该组中的所有加权产品。如果您真的需要,最后一行会将其转换为字典。

但不要忘记,对于加权 AVERAGE,您还需要除以可能的总权重。

它给出了结果:

| Benny | 73.333... |
| Nikki | 70        |
| Tom   | 91        |

本尼的分数基本上是通过以下方式得出的:

 var num = (85 * 1.05) + (85 *1.02) + (60 * 1.05) + (60 * 1.02) + (75 * 1.05) + (75 * 1.02);
 var den = 1.05 + 1.02 + 1.05 + 1.02 + 1.05 + 1.02;
 var result = num / den; // 73.33333......

当然,如果你真的想要加权和,而不是加权平均,那么只需在最终的 select 方法中删除分母,并将 from 重命名为wtAvgto wtSum


推荐阅读