[c#] 하나의 IEnumerable에 다른 IEnumerable의 모든 요소가 포함되어 있는지 확인하십시오.

두 컬렉션에서 각 요소의 필드 / 속성을 비교할 때 하나의 IEnumerable에 다른 IEnumerable의 모든 요소가 포함되어 있는지 확인하는 가장 빠른 방법은 무엇입니까?


public class Item
{
    public string Value;

    public Item(string value)
    {
        Value = value;
    }
}

//example usage

Item[] List1 = {new Item("1"),new Item("a")};
Item[] List2 = {new Item("a"),new Item("b"),new Item("c"),new Item("1")};

bool Contains(IEnumerable<Item> list1, IEnumerable<Item>, list2)
{
    var list1Values = list1.Select(item => item.Value);
    var list2Values = list2.Select(item => item.Value);

    return //are ALL of list1Values in list2Values?
}

Contains(List1,List2) // should return true
Contains(List2,List1) // should return false



답변

한 컬렉션의 모든 값이 다른 컬렉션에 포함되어 있는지 여부를 결정하는 일부 상태를 추적하고 유지하지 않는 한이를 수행하는 “빠른 방법”은 없습니다. IEnumerable<T>반대하는 경우에만 Intersect.

var allOfList1IsInList2 = list1.Intersect(list2).Count() == list1.Count();

이 성능은 Intersect()각 목록을 한 번만 열거하기 때문에 매우 합리적이어야합니다 . 또한, 두 번째 전화가 Count()기본 타입이 경우 최적의 것 ICollection<T>보다는 단지보다 IEnumerable<T>.


답변

Except를 사용하여 첫 번째 목록에서 두 번째 목록에있는 모든 값을 제거한 다음 모든 값이 제거되었는지 확인할 수도 있습니다.

var allOfList1IsInList2 = !list1.Except(list2).Any();

이 메서드는 Count ()를 두 번 호출 할 필요가 없다는 장점이 있습니다.


답변

C # 3.5 이상

Enumerable.All<TSource>모든 List2 항목이 List1에 포함되어 있는지 확인하는 데 사용 :

bool hasAll = list2Uris.All(itm2 => list1Uris.Contains(itm2));

이것은 list1에 list2의 모든 항목보다 더 많은 항목이 포함 된 경우에도 작동합니다.


답변

Kent의 대답은 훌륭하고 짧지 만 그가 제공하는 솔루션은 항상 전체 첫 번째 컬렉션에 대한 반복이 필요합니다. 다음은 소스 코드입니다.

public static IEnumerable<TSource> Intersect<TSource>(this IEnumerable<TSource> first, IEnumerable<TSource> second, IEqualityComparer<TSource> comparer)
{
    if (first == null)
        throw Error.ArgumentNull("first");
    if (second == null)
        throw Error.ArgumentNull("second");
    return Enumerable.IntersectIterator<TSource>(first, second, comparer);
}

private static IEnumerable<TSource> IntersectIterator<TSource>(IEnumerable<TSource> first, IEnumerable<TSource> second, IEqualityComparer<TSource> comparer)
{
    Set<TSource> set = new Set<TSource>(comparer);
    foreach (TSource source in second)
        set.Add(source);
    foreach (TSource source in first)
    {
        if (set.Remove(source))
            yield return source;
    }
}

항상 필요한 것은 아닙니다. 그래서 여기 내 해결책이 있습니다.

public static bool Contains<T>(this IEnumerable<T> source, IEnumerable<T> subset, IEqualityComparer<T> comparer)
{
    var hashSet = new HashSet<T>(subset, comparer);
    if (hashSet.Count == 0)
    {
        return true;
    }

    foreach (var item in source)
    {
        hashSet.Remove(item);
        if (hashSet.Count == 0)
        {
            break;
        }
    }

    return hashSet.Count == 0;
}

실제로 ISet<T>( HashSet<T>) 사용에 대해 생각해야합니다 . 필요한 모든 설정 방법이 포함되어 있습니다. IsSubsetOf귀하의 경우.


답변

Linq 연산자 SequenceEqual도 작동합니다 (그러나 동일한 순서에있는 열거 가능 항목에 민감 함)

return list1Uris.SequenceEqual(list2Uris);


답변

답변으로 표시된 솔루션은 반복되는 경우 실패합니다. IEnumerable에 고유 한 값만 포함되어 있으면 통과합니다.

아래 답변은 반복되는 목록 2 개에 대한 것입니다.

        int aCount = a.Distinct().Count();
        int bCount = b.Distinct().Count();

        return aCount == bCount &&
               a.Intersect(b).Count() == aCount;


답변

Array 대신 HashSet을 사용해야합니다.

예:

List1.SetEquals(List2); //returns true if the collections contains exactly same elements no matter the order they appear in the collection

참고

HasSet의 유일한 제한은 List와 같은 인덱스로 항목을 가져 오거나 사전과 같은 Key로 항목을 가져올 수 없다는 것입니다. 당신이 할 수있는 일은 그것들을 열거하는 것입니다 (각각, 동안 등)

그것이 당신을 위해 작동하는지 알려주십시오