그렇습니다. 나는 열거 할 수 있고 명확한 가치를 얻고 싶습니다.
를 사용하면 System.Linq
물론이라는 확장 방법이 Distinct
있습니다. 간단한 경우 다음과 같이 매개 변수없이 사용할 수 있습니다.
var distinctValues = myStringList.Distinct();
좋고 훌륭하지만 동등성을 지정 해야하는 열거 가능한 객체가있는 경우 사용 가능한 유일한 과부하는 다음과 같습니다.
var distinctValues = myCustomerList.Distinct(someEqualityComparer);
항등 비교 자 인수는의 인스턴스 여야합니다 IEqualityComparer<T>
. 물론이 작업을 수행 할 수는 있지만 다소 장황하고 음탕합니다.
내가 기대했던 것은 람다가 걸리는 과부하입니다 .Func <T, T, bool> :
var distinctValues
= myCustomerList.Distinct((c1, c2) => c1.CustomerId == c2.CustomerId);
그러한 확장이 있는지 또는 동등한 해결 방법이 있는지 아는 사람이 있습니까? 아니면 뭔가 빠졌습니까?
또는 IEqualityComparer 인라인을 지정하는 방법이 있습니까?
최신 정보
Anders Hejlsberg 가이 주제에 대한 MSDN 포럼 의 게시물 에 대한 답변을 찾았습니다 . 그는 말한다 :
두 객체가 동일하게 비교 될 때 동일한 GetHashCode 반환 값을 가져야합니다. 그렇지 않으면 Distinct에서 내부적으로 사용되는 해시 테이블이 올바르게 작동하지 않습니다. IEqualityComparer는 Equals 및 GetHashCode의 호환 가능한 구현을 단일 인터페이스로 패키지하기 때문에 IEqualityComparer를 사용합니다.
나는 그것이 의미가 있다고 생각합니다 ..
답변
IEnumerable<Customer> filteredList = originalList
.GroupBy(customer => customer.CustomerId)
.Select(group => group.First());
답변
MoreLINQDistinctBy
에서 원하는 것처럼 보입니다 . 그런 다음 쓸 수 있습니다 :
var distinctValues = myCustomerList.DistinctBy(c => c.CustomerId);
다음은 컷 다운 버전입니다 DistinctBy
(무효 검사 및 자체 키 비교기를 지정할 수있는 옵션 없음).
public static IEnumerable<TSource> DistinctBy<TSource, TKey>
(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
{
HashSet<TKey> knownKeys = new HashSet<TKey>();
foreach (TSource element in source)
{
if (knownKeys.Add(keySelector(element)))
{
yield return element;
}
}
}
답변
물건을 마무리합니다 . 나는 여기에 온 대부분의 사람들이 라이브러리를 사용하지 않고 가능한 최고의 성능으로 가능한 가장 간단한 솔루션을 원한다고 생각합니다 .
(나를 위해 방법으로 허용 된 그룹은 성능 측면에서 과잉이라고 생각합니다.)
다음은 null 값에도 작동 하는 IEqualityComparer 인터페이스를 사용하는 간단한 확장 방법 입니다.
용법:
var filtered = taskList.DistinctBy(t => t.TaskExternalId).ToArray();
확장 방법 코드
public static class LinqExtensions
{
public static IEnumerable<T> DistinctBy<T, TKey>(this IEnumerable<T> items, Func<T, TKey> property)
{
GeneralPropertyComparer<T, TKey> comparer = new GeneralPropertyComparer<T,TKey>(property);
return items.Distinct(comparer);
}
}
public class GeneralPropertyComparer<T,TKey> : IEqualityComparer<T>
{
private Func<T, TKey> expr { get; set; }
public GeneralPropertyComparer (Func<T, TKey> expr)
{
this.expr = expr;
}
public bool Equals(T left, T right)
{
var leftProp = expr.Invoke(left);
var rightProp = expr.Invoke(right);
if (leftProp == null && rightProp == null)
return true;
else if (leftProp == null ^ rightProp == null)
return false;
else
return leftProp.Equals(rightProp);
}
public int GetHashCode(T obj)
{
var prop = expr.Invoke(obj);
return (prop==null)? 0:prop.GetHashCode();
}
}
답변
이와 같은 확장 방법 과부하는 없습니다. 나는 과거에 이것이 좌절감을 느꼈고 일반적 으로이 문제를 다루는 도우미 클래스를 작성합니다. 목표는를로 변환하는 Func<T,T,bool>
것 IEqualityComparer<T,T>
입니다.
예
public class EqualityFactory {
private sealed class Impl<T> : IEqualityComparer<T,T> {
private Func<T,T,bool> m_del;
private IEqualityComparer<T> m_comp;
public Impl(Func<T,T,bool> del) {
m_del = del;
m_comp = EqualityComparer<T>.Default;
}
public bool Equals(T left, T right) {
return m_del(left, right);
}
public int GetHashCode(T value) {
return m_comp.GetHashCode(value);
}
}
public static IEqualityComparer<T,T> Create<T>(Func<T,T,bool> del) {
return new Impl<T>(del);
}
}
이것은 당신이 다음을 쓸 수 있습니다
var distinctValues = myCustomerList
.Distinct(EqualityFactory.Create((c1, c2) => c1.CustomerId == c2.CustomerId));
답변
속기 솔루션
myCustomerList.GroupBy(c => c.CustomerId, (key, c) => c.FirstOrDefault());
답변
이것은 당신이 원하는 것을 할 것이지만 성능에 대해서는 모르겠습니다.
var distinctValues =
from cust in myCustomerList
group cust by cust.CustomerId
into gcust
select gcust.First();
적어도 장황하지 않습니다.
답변
다음은 필요한 것을 수행하는 간단한 확장 방법입니다.
public static class EnumerableExtensions
{
public static IEnumerable<TKey> Distinct<T, TKey>(this IEnumerable<T> source, Func<T, TKey> selector)
{
return source.GroupBy(selector).Select(x => x.Key);
}
}
그들이 이와 같은 독특한 방법을 프레임 워크에 굽지 않은 것은 부끄러운 일입니다.