etc./StackOverFlow

특정 속성에 대한 LINQ의 Distinct()

청렴결백한 만능 재주꾼 2023. 4. 24. 06:48
반응형

질문자 :Patrick Desjardins


나는 그것에 대해 배우기 위해 LINQ를 가지고 놀고 있지만 Distinct 를 사용하는 방법을 알 수 없습니다(단순한 정수 목록은 수행하기가 매우 쉽습니다. 이것은 질문이 아닙니다). 개체의 하나 이상의 속성에 대한 개체 목록에서 Distinct 를 사용하려면 어떻게 해야 합니까?

예: 개체가 Person 경우 Property Id 있습니다. 어떻게 모든 Person을 가져와서 개체의 Id 속성과 함께 Distinct

 Person1: Id=1, Name="Test1" Person2: Id=1, Name="Test1" Person3: Id=2, Name="Test2"

Person1Person3 만 어떻게 얻을 수 있습니까? 그게 가능합니까?

LINQ로 불가능한 경우 .NET 3.5의 일부 속성에 따라 Person 목록을 갖는 가장 좋은 방법은 무엇입니까?



하나 이상의 속성을 기반으로 고유한 목록을 얻으려면 어떻게 합니까?

단순한! 그룹화하고 그룹에서 승자를 선택하려고 합니다.

 List<Person> distinctPeople = allPeople .GroupBy(p => p.PersonId) .Select(g => g.First()) .ToList();

여러 속성에 대해 그룹을 정의하려는 경우 방법은 다음과 같습니다.

 List<Person> distinctPeople = allPeople .GroupBy(p => new {p.PersonId, p.FavoriteColor} ) .Select(g => g.First()) .ToList();

참고: 특정 쿼리 공급자는 각 그룹에 하나 이상의 요소가 있어야 하고 First가 해당 상황에서 호출하기에 적절한 방법이라는 것을 확인할 수 없습니다. 이러한 쿼리 공급자와 함께 작업하는 경우 FirstOrDefault가 쿼리 공급자를 통해 쿼리를 가져오는 데 도움이 될 수 있습니다.


Amy B

편집 : 이것은 이제 MoreLINQ의 일부입니다.

당신이 필요로하는 것은 효과적으로 "distinct-by"입니다. 쓰기가 상당히 쉽지만 이것이 LINQ의 일부라고 생각하지 않습니다.

 public static IEnumerable<TSource> DistinctBy<TSource, TKey> (this IEnumerable<TSource> source, Func<TSource, TKey> keySelector) { HashSet<TKey> seenKeys = new HashSet<TKey>(); foreach (TSource element in source) { if (seenKeys.Add(keySelector(element))) { yield return element; } } }

Id 속성만 사용하여 고유한 값을 찾으려면 다음을 사용할 수 있습니다.

 var query = people.DistinctBy(p => p.Id);

그리고 여러 속성을 사용하려면 평등을 적절하게 구현하는 익명 유형을 사용할 수 있습니다.

 var query = people.DistinctBy(p => new { p.Id, p.Name });

테스트되지 않았지만 작동해야 합니다(최소한 컴파일됨).

키에 대한 기본 비교자를 가정합니다. 동등 비교자를 전달하려면 HashSet 생성자에 전달하면 됩니다.


Jon Skeet

사용하다:

 List<Person> pList = new List<Person>(); /* Fill list */ var result = pList.Where(p => p.Name != null).GroupBy(p => p.Id).Select(grp => grp.FirstOrDefault());

where 는 항목(더 복잡할 수 있음)과 groupby 를 필터링하고 고유한 기능을 수행하도록 select


karcsi

모든 LINQ와 유사하게 보이도록 하려면 쿼리 구문을 사용할 수도 있습니다.

 var uniquePeople = from p in people group p by new {p.ID} //or group by new {p.ID, p.Name, p.Whatever} into mygroup select mygroup.FirstOrDefault();

Chuck Rostance

충분하다고 생각합니다.

 list.Select(s => s.MyField).Distinct();

Ivan

솔루션 먼저 필드별로 그룹화한 다음 첫 번째 또는 기본 항목을 선택합니다.

 List<Person> distinctPeople = allPeople .GroupBy(p => p.PersonId) .Select(g => g.FirstOrDefault()) .ToList();

cahit beyaz

Linq.ToLookup() 사용하여 이 작업을 수행할 수 있습니다. 이렇게 하면 각 고유 키에 대한 값 컬렉션이 생성됩니다. 컬렉션의 첫 번째 항목을 선택하기만 하면 됩니다.

 Persons.ToLookup(p => p.Id).Select(coll => coll.First());

David Fahlander

다음 코드는 기능적으로 Jon Skeet의 답변 과 동일합니다.

.NET 4.5에서 테스트되었으며 이전 버전의 LINQ에서 작동해야 합니다.

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

덧붙여서, Google Code에서 Jon Skeet의 DistinctBy.cs 최신 버전을 확인하십시오.


Contango

다음과 같이 할 수 있도록 Distinct 함수를 확장하는 방법을 설명하는 기사를 작성했습니다.

 var people = new List<Person>(); people.Add(new Person(1, "a", "b")); people.Add(new Person(2, "c", "d")); people.Add(new Person(1, "a", "b")); foreach (var person in people.Distinct(p => p.ID)) // Do stuff with unique list here.

다음은 기사( 현재 웹 아카이브에 있음 )입니다. LINQ 확장 - 고유한 함수에서 속성 지정


Timothy Khouri

개인적으로 다음 클래스를 사용합니다.

 public class LambdaEqualityComparer<TSource, TDest> : IEqualityComparer<TSource> { private Func<TSource, TDest> _selector; public LambdaEqualityComparer(Func<TSource, TDest> selector) { _selector = selector; } public bool Equals(TSource obj, TSource other) { return _selector(obj).Equals(_selector(other)); } public int GetHashCode(TSource obj) { return _selector(obj).GetHashCode(); } }

그런 다음 확장 방법:

 public static IEnumerable<TSource> Distinct<TSource, TCompare>( this IEnumerable<TSource> source, Func<TSource, TCompare> selector) { return source.Distinct(new LambdaEqualityComparer<TSource, TCompare>(selector)); }

마지막으로 의도된 사용법:

 var dates = new List<DateTime>() { /* ... */ } var distinctYears = dates.Distinct(date => date.Year);

이 접근 방식을 사용하여 찾은 이점 IEqualityComparer 를 허용하는 다른 메서드에 대해 LambdaEqualityComparer 클래스를 재사용한다는 것입니다. (아, 그리고 나는 원래 LINQ 구현에 yield


Joel

DistinctBy()를 사용하여 개체 속성으로 고유 레코드를 가져올 수 있습니다. 사용하기 전에 다음 명령문을 추가하기만 하면 됩니다.

Microsoft.Ajax.Utilities 사용

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

 var listToReturn = responseList.DistinctBy(x => x.Index).ToList();

여기서 '색인'은 데이터가 구별되기를 원하는 속성입니다.


Harry .Naeem

여러 속성에 대한 Distinct 메서드가 필요한 경우 내 PowerfulExtensions 라이브러리를 확인할 수 있습니다. 현재는 매우 어린 단계에 있지만 이미 Distinct, Union, Intersect와 같은 메서드를 사용할 수 있습니다.

사용 방법은 다음과 같습니다.

 using PowerfulExtensions.Linq; ... var distinct = myArray.Distinct(x => xA, x => xB);

Andrzej Gis

우리 프로젝트에서 이러한 작업에 직면했을 때 우리는 비교기를 구성하는 작은 API를 정의했습니다.

따라서 사용 사례는 다음과 같았습니다.

 var wordComparer = KeyEqualityComparer.Null<Word>(). ThenBy(item => item.Text). ThenBy(item => item.LangID); ... source.Select(...).Distinct(wordComparer);

API 자체는 다음과 같습니다.

 using System; using System.Collections; using System.Collections.Generic; public static class KeyEqualityComparer { public static IEqualityComparer<T> Null<T>() { return null; } public static IEqualityComparer<T> EqualityComparerBy<T, K>( this IEnumerable<T> source, Func<T, K> keyFunc) { return new KeyEqualityComparer<T, K>(keyFunc); } public static KeyEqualityComparer<T, K> ThenBy<T, K>( this IEqualityComparer<T> equalityComparer, Func<T, K> keyFunc) { return new KeyEqualityComparer<T, K>(keyFunc, equalityComparer); } } public struct KeyEqualityComparer<T, K>: IEqualityComparer<T> { public KeyEqualityComparer( Func<T, K> keyFunc, IEqualityComparer<T> equalityComparer = null) { KeyFunc = keyFunc; EqualityComparer = equalityComparer; } public bool Equals(T x, T y) { return ((EqualityComparer == null) || EqualityComparer.Equals(x, y)) && EqualityComparer<K>.Default.Equals(KeyFunc(x), KeyFunc(y)); } public int GetHashCode(T obj) { var hash = EqualityComparer<K>.Default.GetHashCode(KeyFunc(obj)); if (EqualityComparer != null) { var hash2 = EqualityComparer.GetHashCode(obj); hash ^= (hash2 << 5) + hash2; } return hash; } public readonly Func<T, K> KeyFunc; public readonly IEqualityComparer<T> EqualityComparer; }

자세한 내용은 LINQ의 IEqualityComparer 사이트에 있습니다.


Vladimir Nesterovsky

번개처럼 빠르지는 않지만 다음과 같이 할 수 있습니다.

 people.Where(p => !people.Any(q => (p != q && p.Id == q.Id)));

즉, "목록에 동일한 ID를 가진 다른 사람이 없는 모든 사람을 선택하십시오."

당신의 예에서 그것은 단지 사람 3을 선택한다는 것을 명심하십시오. 앞의 두 사람 중에서 당신이 원하는 것을 어떻게 말해야 할지 모르겠습니다.


mqp

DistinctBy 기능을 얻기 위해 MoreLinq 라이브러리를 프로젝트에 추가하지 않으려면 IEqualityComparer 인수를 Distinct 메서드 오버로드를 사용하여 동일한 최종 결과를 얻을 수 있습니다.

람다 구문을 사용하여 일반 클래스의 두 인스턴스에 대한 사용자 지정 비교를 수행하는 일반 사용자 지정 같음 비교자 클래스를 만드는 것으로 시작합니다.

 public class CustomEqualityComparer<T> : IEqualityComparer<T> { Func<T, T, bool> _comparison; Func<T, int> _hashCodeFactory; public CustomEqualityComparer(Func<T, T, bool> comparison, Func<T, int> hashCodeFactory) { _comparison = comparison; _hashCodeFactory = hashCodeFactory; } public bool Equals(T x, T y) { return _comparison(x, y); } public int GetHashCode(T obj) { return _hashCodeFactory(obj); } }

그런 다음 기본 코드에서 다음과 같이 사용합니다.

 Func<Person, Person, bool> areEqual = (p1, p2) => int.Equals(p1.Id, p2.Id); Func<Person, int> getHashCode = (p) => p.Id.GetHashCode(); var query = people.Distinct(new CustomEqualityComparer<Person>(areEqual, getHashCode));

짜잔! :)

위의 내용은 다음을 가정합니다.

  • Person.Id 속성 int 유형입니다.
  • people 컬렉션에 null 요소가 포함되어 있지 않습니다.

컬렉션에 null이 포함될 수 있는 경우 null을 확인하기 위해 람다를 다시 작성하면 됩니다. 예:

 Func<Person, Person, bool> areEqual = (p1, p2) => { return (p1 != null && p2 != null) ? int.Equals(p1.Id, p2.Id) : false; };

편집하다

이 접근 방식은 Vladimir Nesterovsky의 답변과 유사하지만 더 간단합니다.

또한 Joel의 답변과 유사하지만 여러 속성을 포함하는 복잡한 비교 논리를 허용합니다.

Id 의해서만 다를 수 있는 경우 다른 사용자가 정답을 제공하면 Person 클래스에서 GetHashCode()Equals() 의 기본 구현을 재정의한 다음 out-of-를 사용하기만 하면 됩니다. 모든 중복을 필터링하는 Linq의 -box Distinct()


Caspian Canuck

Equals(object obj)GetHashCode() 메서드 재정의:

 class Person { public int Id { get; set; } public int Name { get; set; } public override bool Equals(object obj) { return ((Person)obj).Id == Id; // or: // var o = (Person)obj; // return o.Id == Id && o.Name == Name; } public override int GetHashCode() { return Id.GetHashCode(); } }

그런 다음 다음을 호출하십시오.

 List<Person> distinctList = new[] { person1, person2, person3 }.Distinct().ToList();

Waldemar Gałęzinowski

곧 출시될 .NET 6에는 Linq의 새로운 DistinctBy() 확장을 사용하는 새로운 솔루션이 있으므로 .NET 6부터 다음을 수행할 수 있습니다.

 var distinctPersonsById = personList.DistinctBy(x => x.Id);

Magnetron

다른 .NET 버전과 호환되는 이 작업을 수행하는 가장 좋은 방법은 Equals 및 GetHash를 재정의하여 이를 처리하는 것입니다(스택 오버플로 질문 참조 이 코드는 고유한 값을 반환합니다. 그러나 내가 원하는 것은 강력한 형식의 컬렉션을 반환하는 것입니다. 익명 유형 ), 그러나 코드 전체에서 일반적인 것이 필요한 경우 이 기사의 솔루션이 좋습니다.


gcoleman0828

List<Person>lst=new List<Person> var result1 = lst.OrderByDescending(a => a.ID).Select(a =>new Player {ID=a.ID,Name=a.Name} ).Distinct();

Arindam

Person.id에서 Equals를 실제로 수행하려면 person의 Equals를 재정의할 수 있어야 합니다. 이것은 당신이 쫓는 행동을 낳아야 합니다.


GWLlosa

확장 방법이 내장되어 있지 않은 이전 .NET 버전을 사용하는 경우 고유한 확장 방법을 정의할 수 있습니다.

 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 personsDist = persons.DistinctBy(item => item.Name);

TOL

아래 코드로 시도해보세요.

 var Item = GetAll().GroupBy(x => x .Id).ToList();

Mohamed Hammam

출처 : http:www.stackoverflow.com/questions/489258/linqs-distinct-on-a-particular-property

반응형