linq를 사용하여 목록에서 중복 제거


314

Items와 수업 이 properties (Id, Name, Code, Price)있습니다.

목록이 Items중복 된 항목으로 채워집니다.

예를 들어 :

1         Item1       IT00001        $100
2         Item2       IT00002        $200
3         Item3       IT00003        $150
1         Item1       IT00001        $100
3         Item3       IT00003        $150

linq를 사용하여 목록에서 중복을 제거하는 방법은 무엇입니까?


나는 또한 항목 클래스에서 속성으로 다른 클래스를 가지고 있습니다
Prasad

당신은 또한 할 수 있습니다 var set = new HashSet<int>(); var uniques = items.Where(x => set.Add(x.Id));. 그렇게하는 것은 범죄입니다 ..
nawfal

답변:


394
var distinctItems = items.Distinct();

일부 속성 만 일치 시키려면 다음과 같이 사용자 정의 등식 비교기를 만듭니다.

class DistinctItemComparer : IEqualityComparer<Item> {

    public bool Equals(Item x, Item y) {
        return x.Id == y.Id &&
            x.Name == y.Name &&
            x.Code == y.Code &&
            x.Price == y.Price;
    }

    public int GetHashCode(Item obj) {
        return obj.Id.GetHashCode() ^
            obj.Name.GetHashCode() ^
            obj.Code.GetHashCode() ^
            obj.Price.GetHashCode();
    }
}

그런 다음 다음과 같이 사용하십시오.

var distinctItems = items.Distinct(new DistinctItemComparer());

Christian, List <my_Custom_Class> 및 List <string>이 ​​있으면 코드의 변경 내용은 무엇입니까? 내 사용자 정의 클래스에는 DCN 번호이고 list <string>에는 DCN 번호 만있는 다양한 항목이 있습니다. 그래서 List <Custom_Class>에 List <string>의 dcn이 포함되어 있는지 확인해야합니다. 예를 들어 List1 = List <Custom_Class> 및 List2 = List <String>이라고 가정하십시오. List1에 2000 개의 항목이 있고 list2에 40000 개의 항목이있는 경우 List1의 600 개 항목이 List2에 존재합니다. 따라서이 경우 list1과 같은 출력 목록으로 1400이 필요합니다. 그래서 표현은 무엇입니까? 미리 감사드립니다

또한 List1에 다양한 항목이 포함되어 있기 때문에 하나 이상의 경우가 있습니다. 다른 항목 값은 다를 수 있지만 DCN은 동일해야합니다. 그래서 제 경우에는 Distinct가 원하는 것을 내놓지 못했습니다.

2
비교 클래스가 매우 유용하다는 것을 알았습니다. 단순한 속성 이름 비교 이외의 논리를 표현할 수 있습니다. 나는 지난달에 새로운 것을 썼습니다 GroupBy.
Christian Hayter

잘 작동하고 새로운 것을 배우고 C # 에서 XoR연산자 ^를 조사하게했습니다 . VB.NET에서 사용 Xor되었지만 처음에는 코드를 확인하기 위해 코드를 두 번 가져 가야했습니다.
atconway

Distinct Comparer를 사용하려고 할 때 발생하는 오류입니다. "LINQ to Entities는 'System.Linq.IQueryable 1[DataAccess.HR.Dao.CCS_LOCATION_TBL] Distinct[CCS_LOCATION_TBL](System.Linq.IQueryable1 [DataAccess.HR.Dao.CCS_LOCATION_TBL], System.Collections.Generic.IEqualityComparer`1 [ DataAccess.HR.Dao.CCS_LOCATION_TBL]) '메소드를 사용하여이 메소드를 상점 표현식으로 변환 할 수 없습니다.
user8128167

600
var distinctItems = items.GroupBy(x => x.Id).Select(y => y.First());

28
고마워-비교 클래스를 작성하지 않기 위해 찾고 있었기 때문에 이것이 효과가 있습니다 :)
Jen

8
+1이 솔루션은 타이 브레이커도 가능합니다. 기준에 따라 중복을 제거하십시오!
Adriano Carneiro

4
그러나 약간의 오버 헤드!
Amirhossein Mehrvarzi

1
그러나 Victor Juri가 아래에서 제안한 것처럼 FirstorDefault를 사용하십시오. 믿을 수 없다, 그 솔루션은 (사용자 정의 평등 비교없이) 매우 간단 할 수있다
CyberHawk

6
여러 속성으로 그룹화 할 수 있습니다. List <XYZ> MyUniqueList = MyList.GroupBy (x => new {x.Column1, x.Column2}). Select (g => g.First ()). ToList ();
Jobi Joshi

41

Distinct 쿼리에서 문제가 발생하면 MoreLinq 를보고 DistinctBy 연산자를 사용하여 id로 고유 한 개체를 선택하십시오.

var distinct = items.DistinctBy( i => i.Id );

1
Linq에는 DistinctBy () 메서드가 없습니다.
Fereydoon Barikzehy

7
@FereydoonBarikzehy 그러나 그는 순수한 Linq에 대해 이야기하고 있지 않습니다. 포스트는 MoreLinq 프로젝트에 linq입니다 ...
Ademar

30

이것이 Linq와 함께 그룹화하는 방법입니다. 도움이 되길 바랍니다.

var query = collection.GroupBy(x => x.title).Select(y => y.FirstOrDefault());

3
@nawfal, 나는 First () 대신에 FirstOrDefault ()를 제안했다
sobelito

23
내가 올바른 생각하면, 사용 FirstOrDefault(가) 경우 여기를 아무런 혜택을 제공하지 않는다 Select즉시 다음 GroupBy빈 그룹 인 존재의 가능성은 (그룹이되었다가 없기 때문에, 단지 파생 된 컬렉션의 내용에서)
로이 어설프게

17

Distinct()기본 평등 비교자를 사용하여 값을 비교한다는 점을 명심 하십시오 . 따라서 그 이상을 원하면 자체 비교기를 구현해야합니다.

예를 보려면 http://msdn.microsoft.com/en-us/library/bb348436.aspx 를 참조 하십시오 .


컬렉션 멤버 유형이 값 유형 중 하나 인 경우 기본 비교기가 작동합니다. 그러나 참조 유형에 대해 csc가 어떤 기본 동등 비교기를 선택합니다. 참조 유형에는 자체 비교기가 있어야합니다.
누리 YILMAZ

16

목록에서 중복 항목을 제거하기위한 세 가지 옵션이 있습니다.

  1. 맞춤 평등 비교자를 사용 Distinct(new DistinctItemComparer())하고 @Christian Hayter가 언급 한대로 사용하십시오.
  2. 를 사용 GroupBy하지만 GroupBy그룹별로 묶는 경우 Id항상 중복 항목이 제거되지 않으므로 모든 열을 기준으로 그룹화해야합니다 . 예를 들어 다음 예를 고려하십시오.

    List<Item> a = new List<Item>
    {
        new Item {Id = 1, Name = "Item1", Code = "IT00001", Price = 100},
        new Item {Id = 2, Name = "Item2", Code = "IT00002", Price = 200},
        new Item {Id = 3, Name = "Item3", Code = "IT00003", Price = 150},
        new Item {Id = 1, Name = "Item1", Code = "IT00001", Price = 100},
        new Item {Id = 3, Name = "Item3", Code = "IT00003", Price = 150},
        new Item {Id = 3, Name = "Item3", Code = "IT00004", Price = 250}
    };
    var distinctItems = a.GroupBy(x => x.Id).Select(y => y.First());

    이 그룹화의 결과는 다음과 같습니다.

    {Id = 1, Name = "Item1", Code = "IT00001", Price = 100}
    {Id = 2, Name = "Item2", Code = "IT00002", Price = 200}
    {Id = 3, Name = "Item3", Code = "IT00003", Price = 150}

    {Id = 3, Name = "Item3", Code = "IT00004", Price = 250}중복으로 간주되므로 잘못되었습니다 . 따라서 올바른 쿼리는 다음과 같습니다.

    var distinctItems = a.GroupBy(c => new { c.Id , c.Name , c.Code , c.Price})
                         .Select(c => c.First()).ToList();

    3. 재정의 EqualGetHashCode품목 클래스 :

    public class Item
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Code { get; set; }
        public int Price { get; set; }
    
        public override bool Equals(object obj)
        {
            if (!(obj is Item))
                return false;
            Item p = (Item)obj;
            return (p.Id == Id && p.Name == Name && p.Code == Code && p.Price == Price);
        }
        public override int GetHashCode()
        {
            return String.Format("{0}|{1}|{2}|{3}", Id, Name, Code, Price).GetHashCode();
        }
    }

    그런 다음 다음과 같이 사용할 수 있습니다.

    var distinctItems = a.Distinct();

11

보편적 인 확장 방법 :

public static class EnumerableExtensions
{
    public static IEnumerable<T> DistinctBy<T, TKey>(this IEnumerable<T> enumerable, Func<T, TKey> keySelector)
    {
        return enumerable.GroupBy(keySelector).Select(grp => grp.First());
    }
}

사용 예 :

var lstDst = lst.DistinctBy(item => item.Key);

매우 깨끗한 접근
Steven Ryssaert

4

이 확장 방법을 사용해보십시오. 잘하면 이것이 도움이 될 수 있습니다.

public static class DistinctHelper
{
    public static IEnumerable<TSource> DistinctBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
    {
        var identifiedKeys = new HashSet<TKey>();
        return source.Where(element => identifiedKeys.Add(keySelector(element)));
    }
}

용법:

var outputList = sourceList.DistinctBy(x => x.TargetProperty);

3
List<Employee> employees = new List<Employee>()
{
    new Employee{Id =1,Name="AAAAA"}
    , new Employee{Id =2,Name="BBBBB"}
    , new Employee{Id =3,Name="AAAAA"}
    , new Employee{Id =4,Name="CCCCC"}
    , new Employee{Id =5,Name="AAAAA"}
};

List<Employee> duplicateEmployees = employees.Except(employees.GroupBy(i => i.Name)
                                             .Select(ss => ss.FirstOrDefault()))
                                            .ToList();

0

또 다른 해결 방법은 아름답 지 않습니다.

RAM 모듈 정보를 기록하기 위해 "GRADE"및 "SPD"라는 두 가지 속성을 가진 "MEMDES"라는 요소가있는 XML 파일이 있습니다. SPD에는 중복 항목이 많이 있습니다.

중복 된 항목을 제거하는 데 사용하는 코드는 다음과 같습니다.

        IEnumerable<XElement> MList =
            from RAMList in PREF.Descendants("MEMDES")
            where (string)RAMList.Attribute("GRADE") == "DDR4"
            select RAMList;

        List<string> sellist = new List<string>();

        foreach (var MEMList in MList)
        {
            sellist.Add((string)MEMList.Attribute("SPD").Value);
        }

        foreach (string slist in sellist.Distinct())
        {
            comboBox1.Items.Add(slist);
        }

-1

IEqualityComparer를 작성하지 않으려면 다음과 같이 시도하십시오.

 class Program
{

    private static void Main(string[] args)
    {

        var items = new List<Item>();
        items.Add(new Item {Id = 1, Name = "Item1"});
        items.Add(new Item {Id = 2, Name = "Item2"});
        items.Add(new Item {Id = 3, Name = "Item3"});

        //Duplicate item
        items.Add(new Item {Id = 4, Name = "Item4"});
        //Duplicate item
        items.Add(new Item {Id = 2, Name = "Item2"});

        items.Add(new Item {Id = 3, Name = "Item3"});

        var res = items.Select(i => new {i.Id, i.Name})
            .Distinct().Select(x => new Item {Id = x.Id, Name = x.Name}).ToList();

        // now res contains distinct records
    }



}


public class Item
{
    public int Id { get; set; }

    public string Name { get; set; }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.