是否有内置的方法来比较IEnumerable <t>(按其元素)?</t>

时间:2010-05-11 14:52:59

标签: c# .net .net-3.5 comparator

我想比较给定类型的元素列表,看看哪个列表是“更大”。

new BuiltInComparer<IEnumerable<int>>().Compare(
    new[] {3,2,3}, 
    new[] {1,2,3})

...将返回1

new BuiltInComparer<IEnumerable<int>>().Compare(
    new[] {1,2,3}, 
    new[] {1,2,4})

...会返回-1等

有没有这样的内置比较器?

4 个答案:

答案 0 :(得分:10)

我认为框架中没有内置任何东西 - 正如Eric所说,你还没有提供比较标准。如果你的意思是“以自然方式比较元素,并假设'缺失'元素小于任何现有元素”(即如果它们在可能的情况下相等则更长的序列击败更短的子序列)那么这样的事情会做它:

public int SequenceCompare<T>(IEnumerable<T> source1, IEnumerable<T> source2)
{
    // TODO: Parameter validation :)
    // You could add an overload with this as a parameter
    IComparer<T> elementComparer = Comparer<T>.Default;       
    using (IEnumerator<T> iterator1 = source1.GetEnumerator())
    using (IEnumerator<T> iterator2 = source2.GetEnumerator())
    {
        while (true)
        {
            bool next1 = iterator1.MoveNext();
            bool next2 = iterator2.MoveNext();
            if (!next1 && !next2) // Both sequences finished
            {
                return 0;
            }
            if (!next1) // Only the first sequence has finished
            {
                return -1;
            }
            if (!next2) // Only the second sequence has finished
            {
                return 1;
            }
            // Both are still going, compare current elements
            int comparison = elementComparer.Compare(iterator1.Current,
                                                     iterator2.Current);
            // If elements are non-equal, we're done
            if (comparison != 0)
            {
                return comparison;
            }
        }
    }
}

答案 1 :(得分:3)

更精致的版本:

public static class EnumerableExtensions
{
    /// <summary>
    /// Performs lexical comparison of 2 IEnumerable collections holding elements of type T. 
    /// </summary>
    /// <typeparam name="T">Type of collection elements.</typeparam>
    /// <param name="first">The first collection to compare.</param>
    /// <param name="second">The second collection to compare.</param>
    /// <returns>A signed integer that indicates the relative values of a and b:
    /// Less than zero: first is less than second;
    /// Zero: first is equal to second;
    /// Greater than zero: first is greater than second.
    /// </returns>
    /// <remarks>
    /// Can be called as either static method: EnumerableExtensions.Compare(a, b) or
    /// extension method: a.Compare(b).
    /// </remarks>
    public static int Compare<T>(this IEnumerable<T> first, IEnumerable<T> second)
    {
        // If one of collection objects is null, use the default Comparer class
        // (null is considered to be less than any other object)
        if (first == null || second == null)
            return Comparer.Default.Compare(first, second);

        var elementComparer = Comparer<T>.Default;
        int compareResult;

        using (var firstEnum = first.GetEnumerator())
        using (var secondEnum = second.GetEnumerator())
        {
            do
            {
                bool gotFirst = firstEnum.MoveNext();
                bool gotSecond = secondEnum.MoveNext();

                // Reached the end of collections => assume equal
                if (!gotFirst && !gotSecond)
                    return 0;

                // Different sizes => treat collection of larger size as "greater"
                if (gotFirst != gotSecond)
                    return gotFirst ? 1 : -1;

                compareResult = elementComparer.Compare(firstEnum.Current, secondEnum.Current);
            } while (compareResult == 0);
        }

        return compareResult;
    }
}

答案 2 :(得分:2)

如果你使用的是.NET 4(并且它听起来不像你),我认为你可以用Enumerable.Zip做一些聪明的事情。类似的东西:

var r = x.Zip(y, comparer.Compare).FirstOrDefault(c => c != 0);

虽然我现在无法看到如何有效地处理较短的一个与较长的一个相同的情况,就此而言。

编辑:如果您只是比较数组(或者不关心测量集合两次),那么我认为您可以简单地添加:

if (r == 0) {
    r = int.Compare(x.Count(), y.Count());
}

您甚至可以将它们组合为:

var r = x.Zip(y, comparer.Compare)
         .Concat(new [] { int.Compare(x.Count(), y.Count()) })
         .FirstOrDefault(c => c != 0)

(如果你使用的是.NET 3.5,那么添加一个Zip扩展方法,因为它很容易编写并且在所有地方都非常有用!我不知道为什么它不包含在最初的Linq版本中。)

答案 3 :(得分:0)

没有内置的比较器。但是,这是经常出现的要求。我在SequenceComparer<T>文章中详细介绍了这个主题;这是一个简化的实现:

reactiveDf