Есть ли способ получить разницу между двумя наборами объектов в С#

Я хочу получить разницу между двумя наборами целых чисел в С#. Учитывая s1 и s2, я хочу вернуть те целые числа, которые находятся в s1, а не в s2. Я могу сделать что-то вроде:

    List<int> s1 = new List<int>();
    List<int> s2 = new List<int>();

    foreach (int i in s1)
    {
        if (s1.Contains(i))
        {
            //
        }
        else
        {
            //
        }
    }

Но мне было интересно, может ли кто-нибудь указать что-нибудь более чистое. Я хотел бы сделать что-то вроде

List<int> omitted = s1.Difference(s2);

Не уверены, существует ли существующий метод или конструкция LINQ, на которую кто-нибудь мог бы указать? Спасибо.


person SiC    schedule 30.04.2009    source источник


Ответы (4)


Я думаю, вам нужен HashSet.Except. То есть вместо списков используйте HashSets, и тогда операция будет доступна. Это лучший тип, если то, что вы представляете, действительно является «набором». (Если у вас уже есть список, вы можете просто создать из него «новый HashSet».)

person Brian    schedule 30.04.2009
comment
классно спасибо. Except() - это метод, который я искал, и я прислушиваюсь к вашему совету по HashSets. - person SiC; 30.04.2009
comment
За исключением функции расширения, нет? - person leppie; 30.04.2009
comment
Существует метод расширения Enumerable.Except для всех IEnumerables. Но у HashSet есть метод Except специально для HashSets. - person Brian; 30.04.2009
comment
Except действительно является методом расширения, который работает с любым IEnumerable (см. .microsoft.com/en-us/library/). Это означает, что он отлично работает в List‹T›. Конечно, если порядок не имеет значения, HashSet /is/ является более подходящим типом. - person Matthew Flaschen; 30.04.2009
comment
Брайан, я так не думаю. Метод, который вы указали выше, является методом расширения. HashSet имеет метод ExceptWith, который деструктивно удаляет элементы в параметре из экземпляра HashSet, но не Except. - person Matthew Flaschen; 30.04.2009

Еще один полезный API, получите симметричную разницу:

HashSet.SymmetricExceptWith()

person Robin Qiu    schedule 17.09.2018

Вот два метода расширения, которые могут пригодиться, когда вам нужно найти неупорядоченные различия между двумя IEnumerable (это более или менее совпадает с ответом, данным оболочкой leppie в методы расширения):

public class EnumerableDifferences<T>
{
    public IEnumerable<T> Added { get; }
    public IEnumerable<T> Removed { get; }

    public EnumerableDifferences(IEnumerable<T> added, IEnumerable<T> removed)
    {
        Added = added;
        Removed = removed;
    }
}

public static class EnumerableExtensions
{
    public static HashSet<TSource> ToHashSet<TSource>(this IEnumerable<TSource> source, IEqualityComparer<TSource> comparer)
    {
        return new HashSet<TSource>(source, comparer);
    }

    public static IEnumerable<TSource> ExceptBy<TSource, TKey>(this IEnumerable<TSource> first, IEnumerable<TSource> second, Func<TSource, TKey> keySelector, IEqualityComparer<TKey> keyComparer = null)
    {
        return first
            .ExceptBy(keySelector, second.Select(keySelector), keyComparer);
    }

    public static IEnumerable<TSource> ExceptBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IEnumerable<TKey> keys, IEqualityComparer<TKey> keyComparer = null)
    {
        var secondKeys = keys.ToHashSet(keyComparer);

        foreach (var firstItem in source)
        {
            var firstItemKey = keySelector(firstItem);

            if (!secondKeys.Contains(firstItemKey))
            {
                yield return firstItem;
            }
        }
    }

    public static EnumerableDifferences<TSource> DifferencesBy<TSource, TKey>(this IEnumerable<TSource> first, IEnumerable<TSource> second, Func<TSource, TKey> keySelector, IEqualityComparer<TKey> keyComparer = null)
    {
        keyComparer = keyComparer ?? EqualityComparer<TKey>.Default;

        var removed = first.ExceptBy(second, keySelector, keyComparer);
        var added = second.ExceptBy(first, keySelector, keyComparer);

        var result = new EnumerableDifferences<TSource>(added, removed);

        return result;
    }

    public static EnumerableDifferences<TSource> Differences<TSource>(this IEnumerable<TSource> first, IEnumerable<TSource> second, IEqualityComparer<TSource> comparer = null)
    {
        return first
            .DifferencesBy(second, x => x, comparer);
    }
}

public static class Program
{
    public static void Main(params string[] args)
    {
        var l1 = new[] { 'a', 'b', 'c' };
        var l2 = new[] { 'a', 'd', 'c' };

        var result = l1.Differences(l2);

        Console.ReadKey();
    }
}
person Natalie Perret    schedule 28.12.2017

person    schedule
comment
Обратите внимание, что здесь используется Enumerable.Except, поэтому вам нужно использовать System.Linq. msdn.microsoft.com/en-us/library/bb300779.aspx - person Brian; 30.04.2009
comment
Проблема? Нет, я просто хотел помочь кому-то, кто мог бы вырезать и вставить этот код и обнаружить, что он не компилируется. - person Brian; 30.04.2009
comment
@ Брайан, то же предостережение относится и к вашему собственному ответу. HashSet не реализует собственный метод Except, поэтому для вашего ответа также требуется System.Linq. - person LukeH; 30.04.2009
comment
@Deviant: Но у вас работает O (n ^ 2), а у меня O (n) :) - person leppie; 01.05.2009
comment
Этот ответ показывает, что вам нужно дважды вызвать Except, чтобы получить как добавленные, так и удаленные элементы. Этот момент легко пропустить. - person Nils Lande; 17.08.2017