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


102

두 컬렉션에서 각 요소의 필드 / 속성을 비교할 때 하나의 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

1
목록은 어느 방향으로 진행됩니까? list1의 모든 항목이 목록 2에 있는지 아니면 list2의 모든 항목이 목록 1에 있는지 확인 하시겠습니까?
마크 바이어스

답변:


138

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

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

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


몇 가지 테스트를 수행했는데이 방법이 다른 방법보다 빠르게 실행되는 것 같습니다. 팁 고마워.
Brandon Zacharie

2
목록에 중복 항목이 있으면 작동하지 않습니다. 예를 들어 441과 414의 char 배열을 비교하면 41이 반환되므로 카운트가 실패합니다.
John

69

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

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

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


이것은 또한 List1에는 있지만 List2에는없는 것을 찾는 데에도 좋습니다.
Homer

16
이것은 list1에 중복 된 값이있는 상황에서 작동합니다. 받아 들여진 대답은 그렇지 않습니다.
dbc

23

C # 3.5 이상

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

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

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


10
Contains()호출 내 호출 의 성능에 영향을 미칩니다 All().
Kent Boogaart

또한 그룹 메서드로 이동할 수도 있습니다. bool hasAll = list2Uris.All (list1Uris.Contains);
jimpanzer 2014 년

IEnumerable <T> 유형의 경우이 솔루션은 n * m 성능을 제공합니다.
Dmitriy Dokshin

5
속기 : bool hasAll = list2Uris.All(list1Uris.Contains);
Illuminator

3

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귀하의 경우.


2

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

return list1Uris.SequenceEqual(list2Uris);

2

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

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

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

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

이것은 모든 중복을 제거하고 실제로 비교하지 않기 때문에 좋은 해결책이 아닙니다.
John

2

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로 항목을 가져올 수 없다는 것입니다. 당신이 할 수있는 일은 그것들을 열거하는 것입니다 (각각, 동안 등)

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


-2

이 방법을 사용하여 두 목록을 비교할 수 있습니다.

    //Method to compare two list
    private bool Contains(IEnumerable<Item> list1, IEnumerable<Item> list2)
    {
        bool result;

        //Get the value
        var list1WithValue = list1.Select(s => s.Value).ToList();
        var list2WithValue = list2.Select(s => s.Value).ToList();

        result = !list1WithValue.Except(list2WithValue).Any();

        return result;
    }

3 년 전에 거의 동일한 답변이 제공되었습니다. stackoverflow.com/a/16967827/5282087
Dragomok
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.