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


525

이 작업을 수행하는 간단한 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).


3
개체의 ID가 실시간으로 변경되지 않아야하므로 ID를 읽기 전용으로 만드는 것이 좋습니다. 물론 테스트 또는 ORM 프레임 워크는 변경이 가능해야합니다.
— 코드 InChaos

답변:


911

이는 다음 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의 솔루션에 대한 것이기 때문에 이러한 옵션에 대해서는 다루지 않습니다. 항상 그렇듯이 프로젝트의 성능 요구 사항에 대한 접근 방식을 평가하십시오.


34
O (n + m) 시간 안에 쉽게 해결할 수있는 문제에 대한 O (n * m) 솔루션이라는 것을 알고 있습니까?
— Niki

32
@nikie, OP는 Linq를 사용하는 솔루션을 요청했습니다. 아마 그는 Linq를 배우려고 노력하고있을 것이다. 질문이 가장 효율적인 방법이라면 내 질문은 반드시 같을 필요는 없습니다.
— 클라우스 비 스코프 페데르센

46
@nikie, 쉬운 솔루션을 공유하고 싶으십니까?
— Rubio

18
이것은 동등하며 따라하기가 더 쉽습니다. var result = peopleList2.Where (p => peopleList1.All (p2 => p2.ID! = p.ID));
— AntonK

28
@ Menol-질문에 올바르게 응답하는 사람을 비판하는 것은 약간 불공평 할 수 있습니다. 사람들은 미래의 사람들이 답을 찾을 수있는 모든 방법과 상황을 예상 할 필요는 없습니다. 실제로, 당신은 그것을 제공하지 않고 대안을 알고 있다고 시간을내는 nikie에게 그것을 지시해야합니다.
— Chris Rogers

396

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));

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


EqualsID를 비교하기 위해 재정의 된 경우에만 작동합니다 .
— 클라우스 비 스코프 페데르센

34
그렇기 때문에 평등을 무시해야한다고 썼습니다. 그러나 나는 그것 없이도 작동하는 예제를 추가했습니다.
— 코드 InChaos

4
Person이 구조체라면 작동합니다. 그러나 Person은 식별하지 않는 "ID"라는 속성을 가지고 있기 때문에 불완전한 클래스로 보입니다. 식별하지 않으면 equal이 재정의되므로 equal ID는 동일한 Person을 의미합니다. Person의 버그가 수정되면이 접근 방식이 더 좋습니다 (버그가 식별자 인 것처럼 오도되지 않는 다른 것으로 "ID"의 이름을 바꾸어 수정하지 않는 한).
— Jon Hanna

2
문자열 (또는 다른 기본 객체) 목록에 대해 이야기하는 경우에도 효과적입니다.이 스레드를 찾았을 때 내가 찾고 있던 것이 었습니다.
— Dan Korn

@ DanKorn 이것은 기본 비교를 위해 int, objects ref, string과 비교할 때보 다 간단한 솔루션입니다.
— Maze

73

또는 부정없이 원한다면 :

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

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

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


5
이 방법 (50,000 개가 넘는 항목 목록)은 모든 방법보다 훨씬 빠릅니다!
— DaveN

5
게으 르기 때문에 더 빠를 수 있습니다. 이것은 아직 실제 작업을 수행하고 있지 않습니다. 실제로 작업을 수행하는 목록을 열거 할 때까지 (ToList를 호출하거나 foreach 루프의 일부로 사용하는 등)
— Xtros

32

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

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

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


감사합니다. 먼저 쿼리 표현식 구문을 방해합니다.
— 일반적인 이름

15

파티에 늦었지만 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에 대한 조언


12

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

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


두 객체를 바인딩하는 속성이 둘 이상인 경우 (SQL 복합 키 생각)이 트릭이 작동하지 않습니다.
— Alrekr

Alrekr- "더 많은 속성을 비교해야하는 경우 더 많은 속성을 비교해야합니다"라는 말이 있다면 그것은 분명합니다.
— Lucas Morgan

8

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

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);

@BrianT의 코드를 사용하여 코드를 사용하도록 변환하려면 어떻게해야합니까?
— Nicke Manarin

0

다음은 구직자에게없는 IT 기술을 익히는 실무 예제입니다.

//Get a list of skills from the Skill table
IEnumerable<Skill> skillenum = skillrepository.Skill;
//Get a list of skills the candidate has                   
IEnumerable<CandSkill> candskillenum = candskillrepository.CandSkill
       .Where(p => p.Candidate_ID == Candidate_ID);             
//Using the enum lists with LINQ filter out the skills not in the candidate skill list
IEnumerable<Skill> skillenumresult = skillenum.Where(p => !candskillenum.Any(p2 => p2.Skill_ID == p.Skill_ID));
//Assign the selectable list to a viewBag
ViewBag.SelSkills = new SelectList(skillenumresult, "Skill_ID", "Skill_Name", 1);

0

먼저 조건이있는 컬렉션에서 ID를 추출합니다.

List<int> indexes_Yes = this.Contenido.Where(x => x.key == 'TEST').Select(x => x.Id).ToList();

둘째, "비교"estament를 사용하여 선택과 다른 ID를 선택하십시오.

List<int> indexes_No = this.Contenido.Where(x => !indexes_Yes.Contains(x.Id)).Select(x => x.Id).ToList();

분명히 x.key! = "TEST"를 사용할 수 있지만 단지 예일뿐입니다


0

일반 FuncEqualityComparer를 작성하면 어디서나 사용할 수 있습니다.

peopleList2.Except(peopleList1, new FuncEqualityComparer<Person>((p, q) => p.ID == q.ID));

public class FuncEqualityComparer<T> : IEqualityComparer<T>
{
    private readonly Func<T, T, bool> comparer;
    private readonly Func<T, int> hash;

    public FuncEqualityComparer(Func<T, T, bool> comparer)
    {
        this.comparer = comparer;
        if (typeof(T).GetMethod(nameof(object.GetHashCode)).DeclaringType == typeof(object))
            hash = (_) => 0;
        else
            hash = t => t.GetHashCode(); 
    }

    public bool Equals(T x, T y) => comparer(x, y);
    public int GetHashCode(T obj) => hash(obj);
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.