1

I want to iterate through each group and set "IsQualified= true" for only first student whose "University= OPQ". (Example: Emity in Group 2, Jacob in Group 3, White in Group 5)

What I tried

        var results= StudentsList
            .GroupBy(x => x.GroupID)
            .Where(x => x.Any(y => y.University == "OPQ"))
            .FirstOrDefault
            .ToList();

      results.ForEach(x => x.IsQualified = true);

      foreach(var item in results){if (item.IsQualified== true){
      Console.WriteLine("Group " + item.GroupID + " , " + item.Student);}}

This does completely the opposite. I change "IsQualified=True" for all items in the 1st group! That's not what I want.

My Code:

List<StudentInfo> StudentsList = new List<StudentInfo>();

// Group 1
StudentsList.Add( new StudentInfo { Student="Gungun", University="cdf", GroupID = 1, IsQualified=false});
StudentsList.Add( new StudentInfo { Student="John", University="abc", GroupID = 1, IsQualified=false});
StudentsList.Add( new StudentInfo { Student="Jack", University="def", GroupID = 1, IsQualified=false} );
StudentsList.Add( new StudentInfo { Student="Peter", University="abc", GroupID = 1, IsQualified=false} );
StudentsList.Add( new StudentInfo { Student="Lemity", University="abc", GroupID = 1, IsQualified=false} );

// Group 2
StudentsList.Add( new StudentInfo { Student="Donald", University="def", GroupID = 2, IsQualified=false} );
StudentsList.Add( new StudentInfo { Student="Olivia", University="ggh", GroupID = 2, IsQualified=false} );
StudentsList.Add( new StudentInfo { Student="Emity", University="OPQ", GroupID = 2, IsQualified=false} );
StudentsList.Add( new StudentInfo { Student="Emma", University="OPQ", GroupID = 2, IsQualified=false} );
StudentsList.Add( new StudentInfo { Student="Alan", University="OPQ", GroupID = 2, IsQualified=false} );

// Group 3
StudentsList.Add( new StudentInfo { Student="Adam", University="yub", GroupID = 3, IsQualified=false} );
StudentsList.Add( new StudentInfo { Student="Jacob", University="OPQ", GroupID = 3, IsQualified=false} );
StudentsList.Add( new StudentInfo { Student="Matthew", University="OPQ", GroupID = 3, IsQualified=false} );
StudentsList.Add( new StudentInfo { Student="Saint", University="abc", GroupID = 3, IsQualified=false} );
StudentsList.Add( new StudentInfo { Student="Joshua", University="qer", GroupID = 3, IsQualified=false} );
StudentsList.Add( new StudentInfo { Student="Aubrey", University="fef", GroupID = 3, IsQualified=false} );

// Group 4
StudentsList.Add( new StudentInfo { Student="Caleb", University="DEF", GroupID = 4, IsQualified=false} );
StudentsList.Add( new StudentInfo { Student="Anna", University="ABC", GroupID = 4, IsQualified=false} );
StudentsList.Add( new StudentInfo { Student="Chill", University="GHI", GroupID = 4, IsQualified=false} );
StudentsList.Add( new StudentInfo { Student="Alexis", University="JKL", GroupID = 4, IsQualified=false} );
StudentsList.Add( new StudentInfo { Student="Zoe", University="MNO", GroupID = 4, IsQualified=false} );

// Group 5
StudentsList.Add( new StudentInfo { Student="Dylan", University="PQR", GroupID = 5, IsQualified=false} );
StudentsList.Add( new StudentInfo { Student="White", University="OPQ", GroupID = 5, IsQualified=false} );
ASh
  • 34,632
  • 9
  • 60
  • 82

3 Answers3

3

Demo on dotnet fiddle

  1. You should adjust Where clause to filter before grouping.
  2. You can get the first item for each group by using First() instead of FirstOrDefault(). Simply because the Where clause has already filtered to make sure that each group will have at least one item.
  3. In terms of Performance, According to @Çöđěxěŕ's comment, Instead of iterating through the LINQ results (to update), You just update records on the fly as part of the query. It means no need for this code results.ForEach(x => x.IsQualified = true); anymore.
    var results = StudentsList
            .Where(x => x.University == "OPQ")
            .GroupBy(x => x.GroupID)
            .Select(g => 
                    {
                        var firstItem = g.First();
                        firstItem.IsQualified = true;

                        return firstItem;
                    }).ToList();
Nguyễn Văn Phong
  • 13,506
  • 17
  • 39
  • 56
  • 1
    Instead of iterating through the `linq` results (to update), why not just update records on the fly as part of the query? Of course it's not the issue, just a suggestion :), great answer. – Trevor Feb 17 '20 at 14:08
  • 1
    You're right. I've updated my answer, please take a look at – Nguyễn Văn Phong Feb 17 '20 at 14:11
  • 1
    You did help me in the morning too. Thanks :) –  Feb 17 '20 at 14:11
  • 1
    @Phong that's more like it :) The answer is great along with an improvement besides the issue; I like seeing answers like this. – Trevor Feb 17 '20 at 14:11
  • @Phong Sorry to interrupt you. Is it possible to add one more condition? Instead of: ".... for only first student whose University= OPQ"... Can we make it- ".... for only first student whose University= OPQ and same group don't contain any student with the name Adam." So the output shall only be Emity and White (since Jacob's group already contains Adam) I tried this way but it didn't work: ".Where(x => x.University == "OPQ" && x.Student!="Adam")" I also tried using .Where and .Any inside the "Select" clauses with no luck. –  Feb 17 '20 at 17:48
  • 1
    To keep the simple question (For the reader in the future), you can create a new question and tag me. I'll help you. – Nguyễn Văn Phong Feb 18 '20 at 02:19
  • @Phong Hi, will you be able to answer this? https://stackoverflow.com/questions/60359312/store-linq-function-into-variable-define-on-the-fly?noredirect=1#comment106773551_60359312 –  Feb 23 '20 at 05:39
3
var firstPerGroup = StudentsList
                .Where(s => s.University == "OPQ")
                .GroupBy(s => s.GroupID)
                .Select(g => g.First())
                .ToList();
Tomas Chabada
  • 2,869
  • 1
  • 17
  • 18
  • 1
    While this might answer the question, you should add more explanation to help the OP know the reason why. – Nguyễn Văn Phong Feb 17 '20 at 13:46
  • 3
    `g.First()` is enough since we can't have empty groups here; – Dmitry Bychenko Feb 17 '20 at 13:46
  • 1
    By *swapping* `Where` and `GroupBy` we can *guarantee* at least 1 student in each `group`, it's a crucial moment in this solution. On the other hand `GroupBy` followed `Where` (in the original code in the question) can eliminate all the items within a group, so `FirstOrDefault` returns `null` and we are in trouble – Dmitry Bychenko Feb 17 '20 at 13:48
  • 1
    Sure, @Phong answer is more complete, I just posted the main thought. I can update it, but must confirm that Phong was few seconds faster than me, therefore I gave him my UP vote :) – Tomas Chabada Feb 17 '20 at 13:50
  • 1
    Thank you so much @TomasChabada and #Phong both of you. I wish I could accept both of your answers as best :) –  Feb 17 '20 at 14:10
0
 var results = StudentsList.Where(x => x.University == "OPQ").GroupBy(x => x.GroupID).ToList();
            foreach (var item in results)
            {
                Console.WriteLine("Group " + item.Key + " , " + item.FirstOrDefault().Student); 
            }

You should first filter students by university, and then group by groupID. You will get a structure in which the key is the group number, and the value is a list of StudentInfo objects. From this list you need to choose the first student.