Is there a way to remove the for loops using linq to solve the problem I have
I want to get the subject and sum of points for each student and each subject in this list:
IEnumerable<Student> students = new List<Student> {
new Student() {Id = 1, Name = "John", Age = 13},
new Student() {Id = 2, Name = "Mary", Age = 12},
new Student() {Id = 3, Name = "Anne", Age = 14}
};
IEnumerable<StudentScore> studentScores = new List<StudentScore> {
new StudentScore() {StudentId = 1, Subject = "Maths", Points = 54},
new StudentScore() {StudentId = 1, Subject = "Maths", Points = 32},
new StudentScore() {StudentId = 1, Subject = "English", Points = 55},
new StudentScore() {StudentId = 1, Subject = "English", Points = 54},
new StudentScore() {StudentId = 2, Subject = "Maths", Points = 44},
new StudentScore() {StudentId = 2, Subject = "Maths", Points = 37},
new StudentScore() {StudentId = 2, Subject = "English", Points = 59},
new StudentScore() {StudentId = 2, Subject = "English", Points = 64},
new StudentScore() {StudentId = 3, Subject = "Maths", Points = 53},
new StudentScore() {StudentId = 3, Subject = "Maths", Points = 72},
new StudentScore() {StudentId = 3, Subject = "English", Points = 54},
new StudentScore() {StudentId = 3, Subject = "English", Points = 59},
};
foreach (var student in students)
{
foreach (var studentScore in studentScores.Select(ss=>ss.Subject).Distinct())
{
Console.WriteLine("Name: " + student.Name + " Subject:" + studentScore + "Score: " + studentScores.Where(ss => ss.StudentId == student.Id)
.Where(ss => ss.Subject == studentScore)
.Sum(ss => ss.Points));
}
}
Your solution has both a belt and suspenders - the foreach
loops are combined with LINQ, where LINQ part is dependent on the values through which you go in your foreach
loop.
The trick to understanding how this could be done entirely with LINQ is realization that LINQ deals with expressions, not statements. Therefore, you need to produce the whole list at once, and then either print it in a single foreach
loop, or to use string.Format
method to avoid loops altogether.
Here is how you prepare your data:
var rows = students
.Join(
studentScores
, st => st.Id
, sc => sc.StudentId
, (st, sc) => new { Student = st, Score = sc }
)
.GroupBy(p => new { p.Student.Id, p.Score.Subject })
.Select(g => new {
Name = g.First().Student.Name
, Subj = g.Key.Subject
, Points = g.Sum(p => p.Score.Points)
})
.ToList();
Now you can go through this in a foreach
loop, and print prepared results:
foreach (var r in rows) {
Console.WriteLine($"Name: {r.Name} Subject: {r.Subject} Score: {r.Score}");
}