[C#] LINQ를 사용하여 하나의 List <>에서 다른 List <>에없는 항목을 가져옵니다.

이 작업을 수행하는 간단한 LINQ 쿼리가 있다고 가정합니다. 어떻게 정확히 모르겠습니다.

이 코드 조각이 주어지면 :

class Program
{
    static void Main(string[] args)
    {
        List<Person> peopleList1 = new List<Person>();
        peopleList1.Add(new Person() { ID = 1 });
        peopleList1.Add(new Person() { ID = 2 });
        peopleList1.Add(new Person() { ID = 3 });

        List<Person> peopleList2 = new List<Person>();
        peopleList2.Add(new Person() { ID = 1 });
        peopleList2.Add(new Person() { ID = 2 });
        peopleList2.Add(new Person() { ID = 3 });
        peopleList2.Add(new Person() { ID = 4 });
        peopleList2.Add(new Person() { ID = 5 });
    }
}

class Person
{
    public int ID { get; set; }
}

peopleList2에없는 사람들을 모두에게 제공하기 위해 LINQ 쿼리를 수행하고 싶습니다 peopleList1.

이 예는 두 사람을 제공해야합니다 (ID = 4 & ID = 5).



답변

이는 다음 LINQ 표현식을 사용하여 해결할 수 있습니다.

var result = peopleList2.Where(p => !peopleList1.Any(p2 => p2.ID == p.ID));

LINQ를 통해이를 표현하는 다른 방법으로 일부 개발자는 더 읽기 쉽습니다.

var result = peopleList2.Where(p => peopleList1.All(p2 => p2.ID != p.ID));

경고 : 의견에서 언급 한 바와 같이, 이러한 접근 방식은 O (n * m) 작업을 요구합니다. 문제는 없지만 성능 문제가 발생할 수 있으며 특히 데이터 세트가 매우 큰 경우에 발생할 수 있습니다. 이것이 성능 요구 사항을 충족하지 않으면 다른 옵션을 평가해야 할 수도 있습니다. 명시된 요구 사항은 LINQ의 솔루션에 대한 것이기 때문에 이러한 옵션에 대해서는 다루지 않습니다. 항상 그렇듯이 프로젝트의 성능 요구 사항에 대한 접근 방식을 평가하십시오.


답변

People의 평등을 무시하면 다음을 사용할 수도 있습니다.

peopleList2.Except(peopleList1)

ExceptWhere(...Any)두 번째 목록을 해시 테이블에 넣을 수 있으므로 변형 보다 훨씬 빠릅니다 . Where(...Any)의 런타임이있는 O(peopleList1.Count * peopleList2.Count)반면 HashSet<T>(거의) 기반 변형 은의 런타임이 O(peopleList1.Count + peopleList2.Count)있습니다.

Except중복을 암시 적으로 제거합니다. 귀하의 경우에는 영향을 미치지 않지만 유사한 경우에는 문제가 될 수 있습니다.

또는 빠른 코드를 원하지만 동등성을 재정의하고 싶지 않은 경우 :

var excludedIDs = new HashSet<int>(peopleList1.Select(p => p.ID));
var result = peopleList2.Where(p => !excludedIDs.Contains(p.ID));

이 변형은 중복을 제거하지 않습니다.


답변

또는 부정없이 원한다면 :

var result = peopleList2.Where(p => peopleList1.All(p2 => p2.ID != p.ID));

기본적으로 peopleList1의 모든 ID가 peoplesList2의 id와 다른 peopleList2에서 모두 가져옵니다.

a

허용 된 답변과 약간 다른 접근 방식 🙂


답변

지금까지 모든 솔루션이 유창한 구문을 사용했기 때문에 다음은 관심있는 사람들을위한 쿼리 표현식 구문의 솔루션입니다.

var peopleDifference =
  from person2 in peopleList2
  where !(
      from person1 in peopleList1
      select person1.ID
    ).Contains(person2.ID)
  select person2;

나는 그것이 일부 사람들에게 관심을 가질만한 대답과 충분히 다르다고 생각하며 심지어 목록에 대해 차선책이라고 생각했습니다. 이제 색인화 된 ID가있는 테이블의 경우 이것이 확실합니다.


답변

파티에 늦었지만 Linq to SQL 호환 가능한 좋은 솔루션은 다음과 같습니다.

List<string> list1 = new List<string>() { "1", "2", "3" };
List<string> list2 = new List<string>() { "2", "4" };

List<string> inList1ButNotList2 = (from o in list1
                                   join p in list2 on o equals p into t
                                   from od in t.DefaultIfEmpty()
                                   where od == null
                                   select o).ToList<string>();

List<string> inList2ButNotList1 = (from o in list2
                                   join p in list1 on o equals p into t
                                   from od in t.DefaultIfEmpty()
                                   where od == null
                                   select o).ToList<string>();

List<string> inBoth = (from o in list1
                       join p in list2 on o equals p into t
                       from od in t.DefaultIfEmpty()
                       where od != null
                       select od).ToList<string>();

http://www.dotnet-tricks.com/Tutorial/linq/UXPF181012-SQL-Joins-with-C에 대한 조언


답변

클라우스의 대답은 훌륭했지만 ReSharper는 “LINQ 표현 단순화”를 요청합니다.

var result = peopleList2.Where(p => peopleList1.All(p2 => p2.ID != p.ID));


답변

이 열거 가능 확장 기능을 사용하면 제외 할 항목 목록과 비교를 수행하는 데 사용할 키를 찾는 데 사용할 함수를 정의 할 수 있습니다.

public static class EnumerableExtensions
{
    public static IEnumerable<TSource> Exclude<TSource, TKey>(this IEnumerable<TSource> source,
    IEnumerable<TSource> exclude, Func<TSource, TKey> keySelector)
    {
       var excludedSet = new HashSet<TKey>(exclude.Select(keySelector));
       return source.Where(item => !excludedSet.Contains(keySelector(item)));
    }
}

이런 식으로 사용할 수 있습니다

list1.Exclude(list2, i => i.ID);