Programing

IEnumerable.Intersect ()를 사용하여 여러 목록의 교차

lottogame 2020. 10. 19. 07:38
반응형

IEnumerable.Intersect ()를 사용하여 여러 목록의 교차


다음과 같이 교차점을 찾고 싶은 목록이 있습니다.

var list1 = new List<int>() { 1, 2, 3 };
var list2 = new List<int>() { 2, 3, 4 };
var list3 = new List<int>() { 3, 4, 5 };
var listOfLists = new List<List<int>>() { list1, list2, list3 };

// expected intersection is List<int>() { 3 };

IEnumerable.Intersect ()로 이것을 수행하는 방법이 있습니까?

편집 : 나는 이것에 대해 더 명확해야했습니다. 정말 목록이 있습니다. 얼마나 많을 지 모르겠습니다. 위의 세 가지 목록은 단지 예였습니다. IEnumerable<IEnumerable<SomeClass>>

해결책

모든 훌륭한 답변에 감사드립니다. 이 문제를 해결하기위한 네 가지 옵션이 있습니다 : List + aggregate (@Marcel Gosselin), List + foreach (@JaredPar, @Gabe Moothart), HashSet + aggregate (@jesperll) 및 HashSet + foreach (@Tony the Pony). 이 솔루션에 대한 성능 테스트를 수행했습니다 ( 목록 수 , 각 목록 의 요소 난수 최대 크기 변경).

대부분의 상황에서 HashSet은 List보다 성능이 더 좋습니다 (내가 추측하는 HashSet의 특성 때문에 큰 목록과 작은 난수 크기 제외). 방법 (foreach 방법이 약간 더 잘 수행 됩니다.)

나에게 집계 방법은 정말 매력적이지만 (그리고 나는 그것을 받아 들인 대답으로 갈 것입니다) 가장 읽기 쉬운 해결책이라고 말하지 않을 것입니다 .. 다시 한번 감사드립니다!


어때 :

var intersection = listOfLists
    .Skip(1)
    .Aggregate(
        new HashSet<T>(listOfLists.First()),
        (h, e) => { h.IntersectWith(e); return h; }
    );

그렇게하면 전체적으로 동일한 HashSet을 사용하여 단일 문에서 최적화됩니다. listOfLists에 항상 하나 이상의 목록이 포함되어 있는지 확인하십시오.


실제로 Intersect두 번 사용할 수 있습니다 . 그러나 이것이 더 효율적이라고 생각합니다.

HashSet<int> hashSet = new HashSet<int>(list1);
hashSet.IntersectWith(list2);
hashSet.IntersectWith(list3);
List<int> intersection = hashSet.ToList();

물론 작은 세트의 문제는 아니지만 큰 세트가 많은 경우 중요 할 수 있습니다.

기본적으로 Enumerable.Intersect각 호출마다 세트를 생성해야합니다. 세트 작업을 더 많이 수행 할 것이라는 것을 알고 있다면 해당 세트를 유지하는 것이 좋습니다.

그 어느 때보 다 성능 대 가독성을 면밀히 주시하십시오 Intersect. 두 번 호출하는 메서드 체인 은 매우 매력적입니다.

편집 : 업데이트 된 질문 :

public List<T> IntersectAll<T>(IEnumerable<IEnumerable<T>> lists)
{
    HashSet<T> hashSet = null;
    foreach (var list in lists)
    {
        if (hashSet == null)
        {
            hashSet = new HashSet<T>(list);
        }
        else
        {
            hashSet.IntersectWith(list);
        }
    }
    return hashSet == null ? new List<T>() : hashSet.ToList();
}

또는 비어 있지 않고 Skip이 상대적으로 저렴하다는 것을 알고 있다면 :

public List<T> IntersectAll<T>(IEnumerable<IEnumerable<T>> lists)
{
    HashSet<T> hashSet = new HashSet<T>(lists.First());
    foreach (var list in lists.Skip(1))
    {
        hashSet.IntersectWith(list);
    }
    return hashSet.ToList();
}

이것을 시도해보십시오, 작동하지만 집계에서 .ToList ()를 제거하고 싶습니다.

var list1 = new List<int>() { 1, 2, 3 };
var list2 = new List<int>() { 2, 3, 4 };
var list3 = new List<int>() { 3, 4, 5 };
var listOfLists = new List<List<int>>() { list1, list2, list3 };
var intersection = listOfLists.Aggregate((previousList, nextList) => previousList.Intersect(nextList).ToList());

최신 정보:

@pomber의 설명에 ToList()따라 Aggregate호출 내부를 제거 하고 외부로 이동하여 한 번만 실행할 수 있습니다. 이전 코드가 새 코드보다 빠른지 여부를 테스트하지 않았습니다. 필요한 변경 사항은 Aggregate다음과 같이 마지막 줄에 메서드 의 제네릭 유형 매개 변수를 지정하는 것입니다.

var intersection = listOfLists.Aggregate<IEnumerable<int>>(
   (previousList, nextList) => previousList.Intersect(nextList)
   ).ToList();

다음을 수행 할 수 있습니다.

var result = list1.Intersect(list2).Intersect(list3).ToList();

이것은 IntersectMany라고 부르는 확장 메서드가있는 솔루션의 내 버전입니다.

public static IEnumerable<TResult> IntersectMany<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, IEnumerable<TResult>> selector)
{
    using (var enumerator = source.GetEnumerator())
    {
        if(!enumerator.MoveNext())
            return new TResult[0];

        var ret = selector(enumerator.Current);

        while (enumerator.MoveNext())
        {
            ret = ret.Intersect(selector(enumerator.Current));
        }

        return ret;
    }
}

따라서 사용법은 다음과 같습니다.

var intersection = (new[] { list1, list2, list3 }).IntersectMany(l => l).ToList();

이것은 교차 기능이없는 List of List (ListOfLists)에 대한 한 행 솔루션입니다.

var intersect = ListOfLists.SelectMany(x=>x).Distinct().Where(w=> ListOfLists.TrueForAll(t=>t.Contains(w))).ToList()

.net 4 이상에서 작동합니다.


After searching the 'net and not really coming up with something I liked (or that worked), I slept on it and came up with this. Mine uses a class (SearchResult) which has an EmployeeId in it and that's the thing I need to be common across lists. I return all records that have an EmployeeId in every list. It's not fancy, but it's simple and easy to understand, just what I like. For small lists (my case) it should perform just fine—and anyone can understand it!

private List<SearchResult> GetFinalSearchResults(IEnumerable<IEnumerable<SearchResult>> lists)
{
    Dictionary<int, SearchResult> oldList = new Dictionary<int, SearchResult>();
    Dictionary<int, SearchResult> newList = new Dictionary<int, SearchResult>();

    oldList = lists.First().ToDictionary(x => x.EmployeeId, x => x);

    foreach (List<SearchResult> list in lists.Skip(1))
    {
        foreach (SearchResult emp in list)
        {
            if (oldList.Keys.Contains(emp.EmployeeId))
            {
                newList.Add(emp.EmployeeId, emp);
            }
        }

        oldList = new Dictionary<int, SearchResult>(newList);
        newList.Clear();
    }

    return oldList.Values.ToList();
}

Here's an example just using a list of ints, not a class (this was my original implementation).

static List<int> FindCommon(List<List<int>> items)
{
    Dictionary<int, int> oldList = new Dictionary<int, int>();
    Dictionary<int, int> newList = new Dictionary<int, int>();

    oldList = items[0].ToDictionary(x => x, x => x);

    foreach (List<int> list in items.Skip(1))
    {
        foreach (int i in list)
        {
            if (oldList.Keys.Contains(i))
            {
                newList.Add(i, i);
            }
        }

        oldList = new Dictionary<int, int>(newList);
        newList.Clear();
    }

    return oldList.Values.ToList();
}

This is a simple solution if your lists are all small. If you have larger lists, it's not as performing as hash set:

public static IEnumerable<T> IntersectMany<T>(this IEnumerable<IEnumerable<T>> input)
{
    if (!input.Any())
        return new List<T>();

    return input.Aggregate(Enumerable.Intersect);
}

참고URL : https://stackoverflow.com/questions/1674742/intersection-of-multiple-lists-with-ienumerable-intersect

반응형