Thursday, November 10, 2011

Creating LINQ UnorderedEqual


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));
        }
    }
}

No comments:

Post a Comment