Functional programming with Linq — Enumerable.SequenceEqual

Yet anoth­er use­ful method on the Enu­mer­able class, the SequenceE­qual method does exact­ly what it says on the tin and tells you whether or not two sequences are of equal length and their cor­re­spond­ing ele­ments are equal accord­ing to either the default or sup­plied equal­i­ty com­par­er:

var list1 = new List<int>() {0 ,1 ,2, 3, 4, 5, 6 };
var list2 = new List<int>() {0 ,1 ,2, 3, 4, 5, 6 };
var list3 = new List<int>() {6 ,5 ,4, 3, 2, 1, 0 };

list1.SequenceEqual(list2); // returns true
list1.SequenceEqual(list3); // returns false

As you know, for ref­er­ence types the default equal­i­ty com­par­er com­pares the ref­er­ence itself hence:

class Pet
{
    public string Name { get; set; }
    public int Age { get; set; }
}
…
Pet pet1 = new Pet { Name = "Turbo", Age = 2 };
Pet pet2 = new Pet { Name = "Peanut", Age = 8 };

// Create two lists of pets.
var pets1 = new List<Pet> { pet1, pet2 };
var pets2 = new List<Pet> { pet1, pet2 };
var test1 = pets1.SequenceEqual(pets2); // returns true

var pets3 = new List<Pet> { pet1, new Pet { Name = "Peanut", Age = 8 } };
var test2 = pets1.SequenceEqual(pets3); // returns false

There are a num­ber of ways you can get around this, includ­ing:

  • make Pet a val­ue type, i.e. struct
  • make Pet imple­ment the IEquat­able inter­face
  • cre­ate an Equal­i­ty­Compar­er and use the over­loaded SequenceE­qual method which takes an equal­i­ty com­par­er

Here is an inter­est­ing usage of the SequenceE­qual method to help find dupli­cates in a list of lists (see this Stack­Over­flow ques­tion) as pro­vid­ed by Judah Himan­go:

var lists = new List<List<int>>()
{
    new List<int>() {0 ,1, 2, 3, 4, 5, 6 },
    new List<int>() {0 ,1, 2, 3, 4, 5, 6 },
    new List<int>() {0 ,1, 4, 2, 4, 5, 6 },
    new List<int>() {0 ,3, 2, 5, 1, 6, 4 }
};

var duplicates = from list in lists
                 where lists.Except(new[] { list }).Any(l => l.SequenceEqual(list))
                 select list;