Consider using one of the overloads of GroupBy.
With GroupBy you make groups of Students where all Student in the same group have the same value of one or more of the properties. This same value is called the Key.
One of the parameters of GroupBy is the KeySelector. This parameter defines what common property value all Students in a group should have.
Yo want to make groups of Students, where every Student in the group with key "X" has value of property Student.Name equal to "X":
IEnumerable<Student> ungroupedStudents = ...
IEnumerable<IGrouping<string, Student> studentsGroupedByName = ungroupedStudents.GroupBy(
// KeySelector: make groups of students with same name:
student => student.Name);
This makes a sequence of Groups. Every Group has a string property Key, which is the common Name of all Students in the group. The group also is a sequence of Students (be aware: IS not HAS). Every Student in the group has the same value of Student.Name, namely the value of Key.
This looks almost like what you want to achieve. Most readers of your code will know GroupBy, and they expect an IGrouping. So consider to use this as a result.
However, if you really want a sequence of objects, where every object has a string property Name and a property Students, consider to use one of the other overloads of GroupBy, the one that has a parameter resultSelector:
var studentsGroupedByName = ungroupedStudents.GroupBy(
// keySelector: make groups of students with same name:
student => student.Name,
// resultSelector: from every common name, and all students with this name
// make one new object:
(commonName, studentsWithThisName) => new
{
Name = commonName,
Students = studentsWithThisName,
})
This will give you what you want. However, it is not efficient: the group of Student "Charla", which has 25 elements, has 26 times the name "Charla". It would be better not to select this name over and over again:
// resultSelector
(commonName, studentsWithThisName) => new
{
Name = commonName,
Grades = studentsWithThisName.Select(student => new
{
Subject = student.Subject,
Grade = student.Grade,
}
.ToList()
}
Now you really have a sequence of Students with their Grades.
Your database is not normalized
Are you sure that Student names are unique? There won't be two Students named "John Doe" in your collection? If you are in the development phase where you can still change the Students, consider giving each Student a unique Id, similar to what happens in databases.
If you will be using this design for a longer period of time, and will create a lot of Students and Grades, splitting your data into several tables will safe you a lot of troubles in future, if you already have thousands of student grades, and suddenly you get a new student with an already existing name. This will also make it less likely for people to make typing errors ("Mash" instead of "Math"), and even if they do, it will only have to be changed in one place.
I also see problems using a double for Grade. Better use decimal. If you are certain that Grades will have limited number of digits after the decimal point, a decimal is way better in equality comparison than a double. So if you need to get Students with grade equal to 4.7, a decimal is way better to do this, especially if you use calculations to get this value.
class Student
{
public int Id {get; set;}
public string Name {get; set;}
}
public class Grades
{
public int Id {get; set;}
public decimal Grade {get; set;}
... // other properties, for example
public DateTime Date {get; set;}
// foreign keys
public int StudentId {get; set;}
public int SubjectId {get; set;}
}
public class Subject
{
public int Id {get; set;}
public string Name {get; set;}
... // other properties, for instance:
public string description {get; set;}
}