Есть ли встроенный способ сравнения IEnumerable <T>(по их элементам)? - PullRequest
2 голосов
/ 11 мая 2010

Я хотел бы сравнить списки элементов данного типа, чтобы увидеть, какой список"больше".

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 ]

10 голосов
/ 11 мая 2010

Я не думаю, что что-то встроено в фреймворк - и, как говорит Эрик, вы не предоставили критерии сравнения. Если вы имеете в виду «сравнить поэлементно естественным образом и предположить, что« отсутствующий »элемент меньше любого существующего элемента» (т. Е. Более длинная последовательность превосходит более короткую подпоследовательность, если они равны, где это возможно), тогда что-то подобное будет это:

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;
            }
        }
    }
}
3 голосов
/ 13 августа 2013

Более полированная версия:

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 голосов
/ 11 мая 2010

Если вы используете .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 .)

0 голосов
/ 25 марта 2016

Встроенного компаратора нет. Однако это требование, которое возникает часто. Я довольно подробно рассмотрел эту тему в моей статье SequenceComparer<T>; Вот упрощенная реализация:

public class SequenceComparer<TElement> : Comparer<IEnumerable<TElement>>
{
    private readonly IComparer<TElement> _elementComparer;

    public SequenceComparer(IComparer<TElement> elementComparer = null)
    {
        _elementComparer = elementComparer ?? Comparer<TElement>.Default;
    }

    public override int Compare(IEnumerable<TElement> x, IEnumerable<TElement> y)
    {
        // Get enumerators to iterate over both sequences in sync.
        using (IEnumerator<TElement> xEnumerator = x.GetEnumerator())
        using (IEnumerator<TElement> yEnumerator = y.GetEnumerator())
        {
            // Advance both enumerators to their next element, 
            // until at least one passes the end of its sequence.
            bool xMove, yMove;
            while ((xMove = xEnumerator.MoveNext()) &&
                   (yMove = yEnumerator.MoveNext()))
            {
                // Compare the current pair of elements across the two sequences,
                // seeking element inequality.
                int elementComparison = _elementComparer.Compare(xEnumerator.Current, yEnumerator.Current);
                if (elementComparison != 0)
                    return elementComparison;
            }

            // Determine the relative length of the two sequences based on the final values of xMove and yMove.
            return xMove.CompareTo(yMove);
        }
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...