C#에서 한 목록(List)에는 포함되어 있지만 다른 목록에는 없는 항목을 찾아야 하는 경우가 자주 있습니다. 이럴 때 LINQ의 Except 연산자를 사용하면 간단하게 해결할 수 있습니다.
Except 연산자는 LINQ 집합(Set) 연산자 범주에 속하며, 두 개의 컬렉션을 받아 첫 번째 컬렉션 중 두 번째 컬렉션에 존재하지 않는 요소들만 반환합니다.
다만 주의할 점이 있습니다. Except 확장 메서드는 기본 비교자를 사용하기 때문에 클래스와 같은 복합 형식(Complex Type)의 컬렉션에서는 의도한 대로 동작하지 않을 수 있습니다. 이런 경우에는 Where 절을 조합하거나 IEqualityComparer<T>를 구현해야 정확한 결과를 얻을 수 있습니다.
1. Except() 메서드 사용 예제
문자열 목록처럼 단순 형식의 컬렉션이라면 Except() 메서드만으로 충분합니다.
using System;
using System.Collections.Generic;
using System.Linq;
namespace DemoApplication {
class Program {
static void Main(string[] args) {
List<string> animalsList1 = new List<string> {
"tiger", "lion", "dog"
};
Console.WriteLine("List1의 값:");
foreach (var val in animalsList1) {
Console.WriteLine(val);
}
List<string> animalsList2 = new List<string> {
"tiger", "cat", "deer"
};
Console.WriteLine("List2의 값:");
foreach (var val in animalsList2) {
Console.WriteLine(val);
}
var animalsList3 = animalsList1.Except(animalsList2);
Console.WriteLine("List1 중 List2에 없는 값:");
foreach (var val in animalsList3) {
Console.WriteLine(val);
}
Console.ReadLine();
}
}
}tiger는 두 목록 모두에 존재하므로 결과에서 제외되고, List1에만 있는 lion과 dog만 출력됩니다.
출력 결과
List1의 값: tiger lion dog List2의 값: tiger cat deer List1 중 List2에 없는 값: lion dog
2. Where 절을 사용한 예제 (복합 형식)
Fruit 클래스처럼 여러 속성을 가진 객체 목록을 비교할 때는 Except()가 참조를 기준으로 비교하기 때문에 내용이 같은 객체라도 서로 다른 인스턴스면 다른 요소로 판단합니다. 아래 예제처럼 Where 절과 All 연산자를 조합해 특정 속성(Name)을 기준으로 비교하면 원하는 결과를 얻을 수 있습니다.
using System;
using System.Collections.Generic;
using System.Linq;
namespace DemoApplication {
class Program {
static void Main(string[] args) {
List<Fruit> fruitsList1 = new List<Fruit> {
new Fruit { Name = "Apple", Size = "Small" },
new Fruit { Name = "Orange", Size = "Small" }
};
Console.WriteLine("List1의 값:");
foreach (var val in fruitsList1) {
Console.WriteLine(val.Name);
}
List<Fruit> fruitsList2 = new List<Fruit> {
new Fruit { Name = "Apple", Size = "Small" },
new Fruit { Name = "Mango", Size = "Small" }
};
Console.WriteLine("List2의 값:");
foreach (var val in fruitsList2) {
Console.WriteLine(val.Name);
}
var fruitsList3 = fruitsList1.Where(f1 => fruitsList2.All(f2 => f2.Name != f1.Name));
Console.WriteLine("List1 중 List2에 없는 값:");
foreach (var val in fruitsList3) {
Console.WriteLine(val.Name);
}
Console.ReadLine();
}
}
public class Fruit {
public string Name { get; set; }
public string Size { get; set; }
}
}Apple은 두 목록 모두에 존재하므로 제외되고, List1에만 있는 Orange만 출력됩니다.
출력 결과
List1의 값: Apple Orange List2의 값: Apple Mango List1 중 List2에 없는 값: Orange
참고: IEqualityComparer 활용하기
Where 절 대신 Except() 메서드의 두 번째 매개변수로 IEqualityComparer<Fruit>를 구현한 비교자를 전달하는 방법도 있습니다. Equals()와 GetHashCode()를 Name처럼 고유하게 식별 가능한 속성을 기준으로 재정의하면, 복합 형식의 컬렉션에서도 Except() 메서드를 그대로 활용할 수 있어 코드가 더 깔끔해집니다.