使用多列c#linq lambda加入三个列表

时间:2017-01-24 12:27:22

标签: c# asp.net .net linq lambda

我有这些清单:

var subjects = new List<SubjectModel>
        {
            new SubjectModel { subjId = 1, subjName = "Math" },
            new SubjectModel { subjId = 2, subjName = "Science" },
            new SubjectModel { subjId = 3, subjName = "History" },
            new SubjectModel { subjId = 4, subjName = "Language" }
        };

var quizzes = new List<QuizModel>
        {
            new QuizModel { quizId = 1, quizDate = DateTime.Parse("2016-11-25"), quizScore = 10, subjectId = 1 },
            new QuizModel { quizId = 2, quizDate = DateTime.Parse("2016-11-25"), quizScore = 15, subjectId = 1 },
            new QuizModel { quizId = 3, quizDate = DateTime.Parse("2016-11-25"), quizScore = 8, subjectId = 2 },
            new QuizModel { quizId = 4, quizDate = DateTime.Parse("2016-11-26"), quizScore = 13, subjectId = 1 },
            new QuizModel { quizId = 5, quizDate = DateTime.Parse("2016-11-26"), quizScore = 20, subjectId = 2 }
        };

var exams = new List<ExamModel>
        {
            new ExamModel { examId = 1, examDate = DateTime.Parse("2016-11-25"), examScore = 90, subjectId = 1 },
            new ExamModel { examId = 2, examDate = DateTime.Parse("2016-11-25"), examScore = 88, subjectId = 2 },
            new ExamModel { examId = 3, examDate = DateTime.Parse("2016-11-25"), examScore = 92, subjectId = 4 },
            new ExamModel { examId = , examDate = DateTime.Parse("2016-11-26"), examScore = 84, subjectId = 1 },

        };

var exercises = new List<ExerciseModel>
        {
            new ExerciseModel { exerciseId = 1, exerciseDate = DateTime.Parse("2016-11-25"), exerciseScore = 17, subjectId = 1 }, 
            new ExerciseModel { exerciseId = 2, exerciseDate = DateTime.Parse("2016-11-25"), exerciseScore = 15, subjectId = 2 },
            new ExerciseModel { exerciseId = 3, exerciseDate = DateTime.Parse("2016-11-26"), exerciseScore = 15, subjectId = 1 },
            new ExerciseModel { exerciseId = 4, exerciseDate = DateTime.Parse("2016-11-26"), exerciseScore = 12, subjectId = 4 },
            new ExerciseModel { exerciseId = 5, exerciseDate = DateTime.Parse("2016-11-26"), exerciseScore = 10, subjectId = 1 },
        };

我能够按日期和主题成功地对每个人进行分组。

var allQuizzes = quizzes.GroupBy(qz => qz.quizDate, (q, values) =>
            new
            {
                Date = q,
                Quizzes = values.GroupBy(v => v.subjectId, (c, values2) => 
                    new { 
                        SubjectId = c, 
                        QuizSum = values2.Sum(v2 => v2.quizScore) 
                    })
            });

var allExercises = exercises.GroupBy(ex => ex.exerciseDate, (e, values) =>
            new {
                Date = e,
                Exercises = values.GroupBy(x => x.subjectId, (z, values2) =>
                    new {
                        SubjectId = z,
                        ExerSum = values2.Sum(r => r.exerciseScore)
                    })
            });

var allExams = exams.GroupBy(ex => ex.examDate, (e, values) =>
            new
            {
                Date = e,
                Exercises = values.GroupBy(x => x.subjectId, (z, values2) =>
                    new
                    {
                        SubjectId = z,
                        ExamSum = values2.Sum(r => r.examScore)
                    })
            });

但是,我需要加入他们三个以获得所有分数的总和。决赛桌应该像这样显示。

-----------------------------------------------------------------
|      Date     |  Math  |  Science  |   History   |  Language  |
|   11/25/2016  |   132  |      111  |         0   |        92  |
|   11/26/2016  |   122  |       20  |         0   |        12  |
-----------------------------------------------------------------

我尝试加入它们,但它似乎无法通过多列连接。

4 个答案:

答案 0 :(得分:2)

我从同一个匿名类的形式中选择所有3个集合结果(同一个Idea在第一个答案中有Andrei),这样我就可以在所有列表中收集所有结果,而无需映射和转换。

var allQuiz = quizzes.GroupBy(x => new { x.subjectId, x.quizDate })
                     .Select(x => new {
                          Date = x.Key.quizDate, 
                          Subj = x.Key.subjectId, 
                          Sum = x.Sum(r=>r.quizScore)});

var allExam= exams.GroupBy(x => new { x.subjectId, x.examDate })
                     .Select(x => new {
                          Date = x.Key.examDate, 
                          Subj = x.Key.subjectId, 
                          Sum = x.Sum(r=>r.examScore)});

var allExc = exercises.GroupBy(x => new { x.subjectId, x.exerciseDate })
                     .Select(x => new {
                          Date = x.Key.exerciseDate, 
                          Subj = x.Key.subjectId, 
                          Sum = x.Sum(r=>r.exerciseScore)});

将所有结果合并在一起:

var all = allQuiz.ToList();
all.AddRange(allExam.ToList());
all.AddRange(allExc.ToList());

var result = all.GroupBy(x => new { x.Date, x.Subj })
                .Select(x => new { x.Key.Date, x.Key.Subj, Sum = x.Sum(s => s.Sum)});

var list = result.GroupBy(r => r.Date).Select(x => new {
       Date = x.Key, 
       Math = x.SingleOrDefault(t=>t.Subj==1)?.Sum ?? 0,
       Science = x.SingleOrDefault(t=>t.Subj==2)?.Sum ?? 0,
       History = x.SingleOrDefault(t=>t.Subj==3)?.Sum ?? 0,
       Language = x.SingleOrDefault(t=>t.Subj==4)?.Sum ?? 0,
       });

LinqPad中的输出:

enter image description here

答案 1 :(得分:0)

这是一个想法。您可以将所有三个转换为相同的结构,而不是在分组时保持区别。例如:

var allQuizzes = quizzes.GroupBy(qz => qz.quizDate, (q, values) =>
        new
        {
            Date = q,
            Results = values.GroupBy(v => v.subjectId, (c, values2) => 
                new { 
                    SubjectId = c, 
                    Sum = values2.Sum(v2 => v2.quizScore) 
                })
        });

通知名称&#34;结果&#34;和&#34;总和&#34; - 您可以将其用于其他两个对象。现在你有三个集合,都是相同的结构:

{
    Date:
    Results: [
         {SubjectId, Sum}
         {SubjectId, Sum}
         ...
    ]
}

由于它们现在都是相同的,你可以停止对它们进行不同的处理,使用UNION合并所有三个,按日期和按主题对它们进行分组。然后你可以迭代主题列表来获取必要的信息,取决于你的意思&#34;决赛桌&#34;。

答案 2 :(得分:0)

这就是我想出的。 它可能不是最佳的优化,但可能足够你。 我在测试中将结果渲染到StringBuilder中。

var result =
quizzes.Select(q => new {SubjectId = q.subjectId, Date = q.quizDate, Score = q.quizScore})
    .Union(exams.Select(e => new {SubjectId = e.subjectId, Date = e.examDate, Score = e.examScore}))
    .Union(exercises.Select(e => new {SubjectId = e.subjectId, Date = e.exerciseDate, Score = e.exerciseScore}))
    .GroupBy(arg => arg.Date,
    (key, values)=> 
    new
    {
        Key = key,
        Scores = values.GroupBy(v => v.SubjectId, (s, values2) => new { SubjectId = s, SumScore = values2.Sum(v2 => v2.Score) })
    });

StringBuilder sb = new StringBuilder("Date\t\t");
foreach (SubjectModel subject in subjects)
{
    sb.Append($"{subject.subjName}\t");
}

sb.AppendLine();

foreach (var record in result)
{
    sb.Append($"{record.Key.ToShortDateString()}\t");
    foreach (SubjectModel subject in subjects)
    {
        int sum = record.Scores.Where(s => s.SubjectId == subject.subjId).Select(s => s.SumScore).DefaultIfEmpty(0).Single();
        sb.Append($"{sum}\t");
    }
    sb.AppendLine();
}

string finalTable = sb.ToString();

答案 3 :(得分:0)

不要使用三个不同的匿名对象来保存结果,而是创建自己的类:

public enum TestType
{
    Quiz,
    Exam,
    Exercise,
}

public class TestScore
{
    public TestType Type { get; set; }
    public DateTime Date { get; set; }
    public int      Score { get; set; }
    public int      SubjectId { get; set; }

    // Constructors - make a TestScore object

    public TestScore(QuizModel q)
    {
        Type      = TestType.Quiz;
        Date      = q.quizDate;
        Score     = q.quizScore;
        SubjectId = q.SubjectId;            
    }

    public TestScore(ExamModel e)
    {
        Type      = TestType.Exam;
        Date      = e.examDate;
        Score     = e.examScore;
        SubjectId = e.SubjectId;            
    }

    public TestScore(ExerciseModel e)
    {
        Type      = TestType.Exercise;
        Date      = e.exerciseDate;
        Score     = e.exerciseScore;
        SubjectId = e.SubjectId;            
    }
}

转换为TestScore:

List<TestScore> scores = new List<TestScore>();

scores.AddRange(quizzes.Select(q => new TestScore(q));
scores.AddRange(exams.Select(e => new TestScore(e));
scores.AddRange(exercises.Select(e => new TestScore(e));

现在您有一个数据源而不是三个,显示结果变得容易。

相关问题