Sam
Sam

Reputation: 30388

LINQ to create unique values of list of sub-objects

I have a list of courses where the course object looks like this:

public class Course
{
   public int Id { get; set; }
   public string CourseName { get; set; }
   public Teacher Teacher { get; set; } = new Teacher();
}

And for clarity, the teacher object looks like this:

public class Teacher
{
   public int Id { get; set; }
   public string Name { get; set; }
   public string Category { get; set; }
}

I'm trying to produce a list of unique teachers. I think I'm almost there but struggling to produce a List.

Here's what I have so far:

var teachers = from c in Courses // Courses is List<Course> with data in it
               group c by new { c.Teacher.Id, c.Teacher.Name }
               into uniqueTeachers
               select uniqueTeachers.ToList();

Looks like the issue is that I need to create a Teacher object in the group by line.

What am I doing wrong here?

Upvotes: 0

Views: 294

Answers (2)

John Wu
John Wu

Reputation: 52250

Traditional

The first Select gets a list of all teachers (non-unique). We then GroupBy teacher ID and take the First instance of Teacher in each group.

var teachers = courses.Select (a => a.Teacher)
                      .GroupBy(a => a.ID)
                      .Select (g => g.First()).ToList();

No IEqualityComparer required.

Extra credit

LINQ? Why not PLINQ? Like, if you have a trillion courses, or something.

var teachers = new ConcurrentDictionary<int, Teacher>();
courses.AsParallel().ForAll(a => teachers.TryAdd(a.Teacher.ID, a.Teacher));

TryAdd will fail (but not throw an exception) if the ID is already in the list, so this will give you the uniqueness. When the code is finished, you will have a dictionary with the ID and an object instance of each teacher. This solution uses multiple threads if it will increase performance.

IEqualityComparer implementation not needed for this solution.

Upvotes: 1

Felipe Oriani
Felipe Oriani

Reputation: 38608

Like BradleyDotNET user commented, you have to implement the IEqualityComparar<T> to take it done. For sample:

public class TeacherEqualityComparer : IEqualityComparer<Teacher>
{    
  public override bool Equals(Teacher x, Teacher y)
  {
      return x.Id == y.Id && x.Name == y.Name;
  }

  public override int GetHashCode((Teacher obj)
  {
     return obj == null ? 0 : obj.Id;
  }
}

And try to use the Distinct:

var teacherComparer = new TeacherEqualityComparer();

var teachers = (from c in Courses
                select c.Teacher).Distinct(teacherComparer);

Upvotes: 2

Related Questions