0

We have below list of students where I we want to compare the two lists(source, destination), by comparing the student names. If student name is change then we need to execute update query, to update new student name.

Public Class Student
{
   public int StudentId { get; set; }
   public int StudentName { get; set; }
}

And we need to compare the student lists with source to destination.

IList<Student> studentList1 = new List<Student>() { 
        new Student() { StudentId = 1, StudentName = "John", Age = 18 } ,
        new Student() { StudentId = 2, StudentName = "Steve",  Age = 15 }

};

IList<Student> studentList2 = new List<Student>() { 
        new Student() { StudentId = 1, StudentName = "Michaiel", Age = 18 } ,
        new Student() { StudentId = 2, StudentName = "Mike",  Age = 15 } ,
};

So, as of now we were thinking to use SequenceEqual method to compare two list. So, is this method also compares for case sensitive values and any other better alternative available to this ?

And this method can be used for other data type like boolean, int etc. ?

Any help on this appreciated !

XamDev
  • 3,377
  • 12
  • 58
  • 97
  • My guess would be to just override the `equals` method. https://stackoverflow.com/questions/9317582/correct-way-to-override-equals-and-gethashcode – aloisdg Oct 21 '19 at 08:35
  • Why would they have the same `StudentId` for different Students? is this a good representation of the data you're working on? – Brett Caswell Oct 21 '19 at 08:48
  • @BrettCaswell I am updating the records by comparing it, so there is no duplication of Ids. – XamDev Oct 21 '19 at 08:51

2 Answers2

3

Like most LINQ methods, SequenceEquals accepts a comparer parameter which allows you to compare elements using different comparisons.

You can implement the IEqualityComparer interface directly or inherit from EqualityComparer :

class StudentNameComparer: EqualityComparer<Student>
{
    public override bool Equals(Student st1, Student st2)
    {
        return String.Equals(st1?.StudentName,st2?.StudentName);
    }

    public override int GetHashCode(Student st)
    {
        return st.Name.GetHashCode();
    }
}

This allows you to check for differences with :

var hasDiffs=studentList1.SequenceEquals(studentList2, new StudentNameComparer());

Except has a comparer too. You can find the actual differences with :

var diffs=studentList1.Except(studentList2,new StudentNameComparer());

String comparison in .NET is case-sensitive. If you want to use case-insensitive comparison you should probably extract and compare the names. You can use String.Equals with the desired StringComparison parameter, eg :String.Equals(st1.StudentName,st2.StudentName,StringComparison.OrdinalIgnoreCase) but what about the hash code?

StringComparer already implements the appropriate comparisons and hash codes, so it's easier to use :

var studentNames1=studentList1.Select(st=>st.StudentName);
var studentNames2=studentList2.Select(st=>st.StudentName);
var haveDiffs=studentNames1.SequenceEqual(studentName2,StringComparer.OrdinalIgnoreCase);
Panagiotis Kanavos
  • 120,703
  • 13
  • 188
  • 236
  • nice.. this was essentially how I was going to answer this question.. defining and using an `EqualityComparer` and passing it into the collection methods based on intent... – Brett Caswell Oct 21 '19 at 09:50
  • Don't you need to take `StudentId` into account? – Rufus L Oct 21 '19 at 10:53
  • 1
    @RufusL isn't the question how to compare by names only? If `Id` is needed, `SequenceEqual` is meaningless - it only works if one assumes the lists are already ordered and the items match. If that doesn't hold, one would have to *join* by ID before comparing, or generate hashsets based on both keys – Panagiotis Kanavos Oct 21 '19 at 11:01
0

If student name is change then we need to execute update query, to update new student name.

Based on that statement, you need to know more than simply if the two lists contain the same members. It seems to me that you should iterate through the first list and see if there's an updated name in the second list:

foreach (var student in studentList1)
{
    var otherStudent = studentList2.FirstOrDefault(other => 
        other.SudentId == student.StudentId && 
        !other.StudentName.Equals(student.StudentName, StringComparison.OrdinalIgnoreCase));

    if (otherStudent != null)
    {
        // Update student record with student.StudentId to otherStudent.StudentName
    }
}

Similarly, you could grab all the students with new names using something like the following:

var studentsWithNewNames = studentList2.Where(s2 =>
    studentList1.Any(s1 =>
        s1.StudentId == s2.StudentId &&
        !s1.StudentName.Equals(s2.StudentName, StringComparison.OrdinalIgnoreCase)));

foreach (var newNameStudent in studentsWithNewNames)
{
    // Update student with newNameStudent.StudentId to newNameStudent.StudentName
}

Or you could go fully linq and just do:

studentList2.Where(s2 => studentList1.Any(s1 =>
    s1.StudentId == s2.StudentId &&
    !s1.StudentName.Equals(s2.StudentName, StringComparison.OrdinalIgnoreCase)))
    .ToList()
    .ForEach(student =>
    {
        // update student.StudentId with student.StudentName
    });

Also note that you should not store Age as a property, since it's constantly changing. You should store DateOfBirth, and then present Age as a calculated field. And Name should be a string (note also that you don't need to repeat the class name in property names):

public class Student
{
    public int Id { get; set; }
    public string Name { get; set; }
    public DateTime DateOfBirth { get; set; }
    public TimeSpan Age => DateTime.Now - DateOfBirth;
}
Rufus L
  • 36,127
  • 5
  • 30
  • 43
  • yea.. this is an answer.. it does need to be added for brevity sake, but it always strikes me as odd to see people loop and compare sets like this.. you could probably do this with `Join` though (or the result set of a join).. or create an extension similar to it. – Brett Caswell Oct 21 '19 at 10:07