Background
A colleague of mine asked me for some help with writing a LINQ query to check if two lists contained the same contents, order-insensitive. I was surprised to find that LINQ has built-in extension methods for a similar order-sensitive check (SequenceEqual) but not an order-insensitive check.
So, after searching StackOverflow, I wrote the following extension method and tests. Use them as you see fit!
The Code
// 2011 © Team Gunmetal, Inc. // Available for reuse under the MIT License: http://www.opensource.org/licenses/mit-license.php using System.Collections.Generic; using System.Linq; namespace TeamGunmetal.Extensions { /// <summary> /// A place to put IEnumerable extension methods /// </summary> public static class IEnumerableExtension { /// <summary> /// Determines whether two unordered sequences are equal by comparing the elements /// using the default equality comparer for their type. /// </summary> /// <typeparam name="T">The type of the elements of the input sequences.</typeparam> /// <param name="first"> /// An IEnumerable<<typeparamref name="T"/>> to compare to <paramref name="second"/>. /// </param> /// <param name="second"> /// An IEnumerable<<typeparamref name="T"/>> to compare to the <paramref name="first"/> sequence. /// </param> /// <returns> /// true if the two source sequences are of equal length and their elements are equal /// according to the default equality comparer for their type, irrespective of order; /// otherwise, false. /// </returns> public static bool UnorderedEqual<T>(this IEnumerable<T> first, IEnumerable<T> second) { var firstCount = first.Count(); return firstCount == second.Count() && firstCount == first.Intersect(second).Count(); } } }
The Tests
// 2011 © Team Gunmetal, Inc. // Available for reuse under the MIT License: http://www.opensource.org/licenses/mit-license.php using System.Collections.Generic; using TeamGunmetal.Extensions; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace TeamGunmetal.Tests.Extensions { [TestClass] public class IEnumerableExtensionTest { [TestMethod] public void UnorderedEqual_IEnumerablesWithSameContentsInSameOrderAreEqual() { IEnumerable<int> list1 = new List<int>() { 1, 2, 3 }; IEnumerable<int> list2 = new List<int>() { 1, 2, 3 }; Assert.IsTrue(list1.UnorderedEqual(list2)); } [TestMethod] public void UnorderedEqual_IEnumerablesWithSameContentsInOppositeOrderAreEqual() { IEnumerable<int> list1 = new List<int>() { 1, 2, 3 }; IEnumerable<int> list2 = new List<int>() { 3, 2, 1 }; Assert.IsTrue(list1.UnorderedEqual(list2)); } [TestMethod] public void UnorderedEqual_IEnumerablesWithSameContentsInRandomOrderAreEqual() { IEnumerable<int> list1 = new List<int>() { 1, 2, 3 }; IEnumerable<int> list2 = new List<int>() { 2, 3, 1 }; Assert.IsTrue(list1.UnorderedEqual(list2)); } [TestMethod] public void UnorderedEqual_IEnumerablesWithoutContentsAreEqual() { IEnumerable<int> list1 = new List<int>(); IEnumerable<int> list2 = new List<int>(); Assert.IsTrue(list1.UnorderedEqual(list2)); } [TestMethod] public void UnorderedEqual_IEnumerablesWithSameContentsButDifferentFrequenciesAreNotEqual() { IEnumerable<int> list1 = new List<int>() { 1, 2, 3, 1, 2, 3 }; IEnumerable<int> list2 = new List<int>() { 1, 2, 3 }; Assert.IsFalse(list1.UnorderedEqual(list2)); } [TestMethod] public void UnorderedEqual_IEnumerablesOfSameLengthButWithMutuallyExclusiveContentsAreNotEqual() { IEnumerable<int> list1 = new List<int>() { 1, 2, 3 }; IEnumerable<int> list2 = new List<int>() { 4, 5, 6 }; Assert.IsFalse(list1.UnorderedEqual(list2)); } [TestMethod] public void UnorderedEqual_IEnumerablesOfDifferentLengthAndWithMutuallyExclusiveContentsAreNotEqual() { IEnumerable<int> list1 = new List<int>() { 1, 2, 3 }; IEnumerable<int> list2 = new List<int>() { 4, 5, 6, 7, 8 }; Assert.IsFalse(list1.UnorderedEqual(list2)); } [TestMethod] public void UnorderedEqual_IEnumerablesOfDifferentLengthsButThatHaveAnIntersectionEqualToOnlyTheFirstAreNotEqual() { IEnumerable<int> list1 = new List<int>() { 1, 2, 3 }; IEnumerable<int> list2 = new List<int>() { 1, 2, 3, 4, 5 }; Assert.IsFalse(list1.UnorderedEqual(list2)); } [TestMethod] public void UnorderedEqual_IEnumerablesOfDifferentLengthsButThatHaveAnIntersectionEqualToOnlyTheSecondAreNotEqual() { IEnumerable<int> list1 = new List<int>() { 1, 2, 3, 4, 5 }; IEnumerable<int> list2 = new List<int>() { 1, 2, 3 }; Assert.IsFalse(list1.UnorderedEqual(list2)); } } }