Home > Back-end >  Add numbering to grouped items in Linq
Add numbering to grouped items in Linq

Time:09-27

I need to add GroupID to students that have duplicate School and duplicate Age (Steve, Bill, Rich, Robert). Output needs to be cast in the original list format (List<Student>).

List<Student> studentList = new List<Student>() { 
    new Student() { StudentID = 1, StudentName = "John", Age = 18, School = "ABC" , GroupID = 0} ,
    new Student() { StudentID = 2, StudentName = "Steve",  Age = 21, School = "DEF", GroupID = 0 } ,
    new Student() { StudentID = 3, StudentName = "Bill",  Age = 21, School = "DEF", GroupID = 0 } ,
    new Student() { StudentID = 4, StudentName = "Josh" , Age = 20, School = "DEF", GroupID = 0 },
    new Student() { StudentID = 5, StudentName = "Jack" , Age = 19, School = "JKL", GroupID = 0 },
    new Student() { StudentID = 6, StudentName = "Thomas" , Age = 18, School = "MNO", GroupID = 0 },
    new Student() { StudentID = 7, StudentName = "Rich" , Age = 22, School = "PQR", GroupID = 0 },
    new Student() { StudentID = 8, StudentName = "Robert" , Age = 22, School = "PQR", GroupID = 0 },
    new Student() { StudentID = 9, StudentName = "John" , Age = 20, School = "PQR", GroupID = 0 },
    new Student() { StudentID = 10, StudentName = "Emma" , Age = 20, School = "XYZ", GroupID = 0 }};

List<Student> outputList = studentList
    .GroupBy(s => new { s.Age, s.School })
    .Where(g => g.Count() >= 2)
    .SelectMany(g => g)
    .ToList();

Output:

  • Steve & Bill: GroupID = 1

  • Rich & Robert: GroupID = 2

Any help is appreciated.

CodePudding user response:

SelectMany has an overload that passes in the index of the item so you could do this:

List<Student> outputList = studentList
    .GroupBy(s => new { s.Age, s.School })
    .Where(g => g.Count() >= 2)
    .SelectMany((g, i) => g.Select(s =>
    {
        s.GroupID = i;
        return s;
    }))
    .ToList();

That does feel a little hacky (I don't like mutating objects in Linq) so I would probably do something like this:

List<Student> outputList = studentList
    .GroupBy(s => new { s.Age, s.School })
    .Where(g => g.Count() >= 2)
    .SelectMany((g, i) => g.Select(s => new Student
    {
        StudentID = s.StudentID,
        StudentName = s.StudentName,
        Age = s.Age,
        School = s.School,
        GroupID = i 
    }))
    .ToList();

CodePudding user response:

There are other ways to do this, however, why not Just use an old school loop ?

var outputList = studentList
   .GroupBy(s => new { s.Age, s.School })
   .Where(g => g.Count() >= 2)
   .ToList();

var count = 0;
foreach (var students in outputList)
{
   count  ;
   foreach (var student in students)
      student.GroupID = count;
}

var result = outputList.SelectMany(x => x);

CodePudding user response:

 var gp = studentList
           .GroupBy(s => new { s.Age, s.School })
           .Select((x, y) => 
               new 
               { 
                   GroupId = y, 
                   Students = x.Select(o => new Student{Age = o.Age ...
               }) 
           }).ToList();

CodePudding user response:

If you want to use LINQ, one way would be to add this method in your Student class:

public Student SetGroupId(int id)
{
    this.GroupID = id;
    return this;
}

Then, you can create the output list like this:

List<Student> outputList = studentList
    .GroupBy(s => new { s.Age, s.School })
    .Where(g => g.Count() >= 2)
    .SelectMany((g, i) => g.Select(s => s.SetGroupId(i   1)))
    .ToList();

You could get rid of the SetGroupId method if you want but it would be less readable, in my opinion:

List<Student> outputList = studentList
    .GroupBy(s => new { s.Age, s.School })
    .Where(g => g.Count() >= 2)
    .SelectMany((g, i) => g.Select(s => { s.GroupID = i   1; return s; }))
    .ToList();
  • Related