여러 필드에 LINQ Distinct ()를 사용하는 방법
데이터베이스에서 파생 된 다음 EF 클래스 가 있습니다 (단순화 됨).
class Product
{
public string ProductId;
public string ProductName;
public string CategoryId;
public string CategoryName;
}
ProductId
테이블 의 기본 키 입니다.
DB 설계자가 잘못된 설계 결정을 내린 경우 (수정할 수 없음) 이 표에 CategoryId
및 CategoryName
있습니다.
나는 필요 DropDownList로 (별개의)와 CategoryId
같은 값 과 CategoryName
같은 텍스트 . 따라서 다음 코드를 적용했습니다.
product.Select(m => new {m.CategoryId, m.CategoryName}).Distinct();
이는 논리적으로 함께 익명의 객체를 생성해야 CategoryId
하고 CategoryName
속성으로. Distinct()
보장 (더 중복 쌍 없는지 CategoryId
, CategoryName
).
그러나 실제로 작동하지 않습니다. Distinct()
컬렉션에 필드가 하나만있을 때 작업을 이해하는 한 그렇지 않으면 무시합니다 ... 정확합니까? 해결 방법이 있습니까? 감사!
최신 정보
죄송합니다 product
:
List<Product> product = new List<Product>();
다음과 같은 결과를 얻을 수있는 다른 방법을 찾았습니다 Distinct()
.
product.GroupBy(d => new {d.CategoryId, d.CategoryName})
.Select(m => new {m.Key.CategoryId, m.Key.CategoryName})
목록에서 메서드 호출처럼 구별을 사용한다고 가정합니다. 예를 들어를 통해 구체화하여 쿼리 결과를 DropDownList의 데이터 소스로 사용해야합니다 ToList
.
var distinctCategories = product
.Select(m => new {m.CategoryId, m.CategoryName})
.Distinct()
.ToList();
DropDownList1.DataSource = distinctCategories;
DropDownList1.DataTextField = "CategoryName";
DropDownList1.DataValueField = "CategoryId";
속성이 거의없는 익명 유형 대신 실제 객체가 필요한 경우 또 다른 방법은 익명 유형을 사용하는 것 GroupBy
입니다.
List<Product> distinctProductList = product
.GroupBy(m => new {m.CategoryId, m.CategoryName})
.Select(group => group.First()) // instead of First you can also apply your logic here what you want to take, for example an OrderBy
.ToList();
세 번째 옵션은 MoreLinq의DistinctBy
.
Distinct ()는 중복 쌍 (CategoryId, CategoryName)이 없음을 보장합니다.
-정확히
익명의 유형은 '마법처럼'구현 Equals
되고GetHashcode
어딘가에 또 다른 오류가 있다고 가정합니다. 대소 문자 구분? 변경 가능한 클래스? 비교할 수없는 필드?
Distinct 메서드는 시퀀스에서 고유 한 요소를 반환합니다.
Reflector를 사용한 구현을 살펴보면 DistinctIterator
익명 유형에 대해 생성된다는 것을 알 수 있습니다. 고유 반복기는 Set
컬렉션을 열거 할 때에 요소를 추가합니다 . 이 열거자는 이미에있는 모든 요소를 건너 뜁니다 Set
. 요소가 이미 존재하는지 정의하기위한 Set
사용 GetHashCode
및 Equals
방법 Set
.
방법 GetHashCode
과 Equals
익명 형식 구현? msdn에 명시된대로 :
익명 형식의 Equals 및 GetHashCode 메서드는 속성의 Equals 및 GetHashcode 메서드 측면에서 정의됩니다. 동일한 익명 형식의 두 인스턴스는 모든 속성이 동일한 경우에만 동일합니다.
따라서 별개의 컬렉션을 반복 할 때 분명히 별개의 익명 객체가 있어야합니다. 결과는 익명 유형에 사용하는 필드 수에 따라 달라지지 않습니다.
Key
선택한 키워드를 사용하면 아래와 같이 작동합니다.
product.Select(m => new {Key m.CategoryId, Key m.CategoryName}).Distinct();
나는 이것이 오래된 스레드를 가져 오는 것을 알고 있지만 일부 사람들에게 도움이 될 것이라고 생각했습니다. .NET으로 작업 할 때 일반적으로 VB.NET으로 코딩하므로 Key
C #으로 다르게 변환 될 수 있습니다.
이것은 내 솔루션이며 다른 유형의 keySelector를 지원합니다.
public static IEnumerable<TSource> DistinctBy<TSource>(this IEnumerable<TSource> source, params Func<TSource, object>[] keySelectors)
{
// initialize the table
var seenKeysTable = keySelectors.ToDictionary(x => x, x => new HashSet<object>());
// loop through each element in source
foreach (var element in source)
{
// initialize the flag to true
var flag = true;
// loop through each keySelector a
foreach (var (keySelector, hashSet) in seenKeysTable)
{
// if all conditions are true
flag = flag && hashSet.Add(keySelector(element));
}
// if no duplicate key was added to table, then yield the list element
if (flag)
{
yield return element;
}
}
}
그것을 사용하려면 :
list.DistinctBy(d => d.CategoryId, d => d.CategoryName)
질문의 헤드 라인에 답하고 (무엇이 사람들을 끌어들 였는지) 예제에서 익명 유형을 사용했다는 사실을 무시합니다 ....
이 솔루션은 비 익명 유형에서도 작동합니다. 익명 유형에는 필요하지 않습니다.
도우미 클래스 :
/// <summary>
/// Allow IEqualityComparer to be configured within a lambda expression.
/// From https://stackoverflow.com/questions/98033/wrap-a-delegate-in-an-iequalitycomparer
/// </summary>
/// <typeparam name="T"></typeparam>
public class LambdaEqualityComparer<T> : IEqualityComparer<T>
{
readonly Func<T, T, bool> _comparer;
readonly Func<T, int> _hash;
/// <summary>
/// Simplest constructor, provide a conversion to string for type T to use as a comparison key (GetHashCode() and Equals().
/// https://stackoverflow.com/questions/98033/wrap-a-delegate-in-an-iequalitycomparer, user "orip"
/// </summary>
/// <param name="toString"></param>
public LambdaEqualityComparer(Func<T, string> toString)
: this((t1, t2) => toString(t1) == toString(t2), t => toString(t).GetHashCode())
{
}
/// <summary>
/// Constructor. Assumes T.GetHashCode() is accurate.
/// </summary>
/// <param name="comparer"></param>
public LambdaEqualityComparer(Func<T, T, bool> comparer)
: this(comparer, t => t.GetHashCode())
{
}
/// <summary>
/// Constructor, provide a equality comparer and a hash.
/// </summary>
/// <param name="comparer"></param>
/// <param name="hash"></param>
public LambdaEqualityComparer(Func<T, T, bool> comparer, Func<T, int> hash)
{
_comparer = comparer;
_hash = hash;
}
public bool Equals(T x, T y)
{
return _comparer(x, y);
}
public int GetHashCode(T obj)
{
return _hash(obj);
}
}
가장 간단한 사용법 :
List<Product> products = duplicatedProducts.Distinct(
new LambdaEqualityComparer<Product>(p =>
String.Format("{0}{1}{2}{3}",
p.ProductId,
p.ProductName,
p.CategoryId,
p.CategoryName))
).ToList();
가장 간단한 (비효율적이지 않은) 사용법은 사용자 지정 해싱을 방지 할 수 있도록 문자열 표현에 매핑하는 것입니다. 동일한 문자열에는 이미 동일한 해시 코드가 있습니다.
참조 :
IEqualityComparer에서 대리자 래핑
public List<ItemCustom2> GetBrandListByCat(int id)
{
var OBJ = (from a in db.Items
join b in db.Brands on a.BrandId equals b.Id into abc1
where (a.ItemCategoryId == id)
from b in abc1.DefaultIfEmpty()
select new
{
ItemCategoryId = a.ItemCategoryId,
Brand_Name = b.Name,
Brand_Id = b.Id,
Brand_Pic = b.Pic,
}).Distinct();
List<ItemCustom2> ob = new List<ItemCustom2>();
foreach (var item in OBJ)
{
ItemCustom2 abc = new ItemCustom2();
abc.CategoryId = item.ItemCategoryId;
abc.BrandId = item.Brand_Id;
abc.BrandName = item.Brand_Name;
abc.BrandPic = item.Brand_Pic;
ob.Add(abc);
}
return ob;
}
Employee emp1 = new Employee() { ID = 1, Name = "Narendra1", Salary = 11111, Experience = 3, Age = 30 };Employee emp2 = new Employee() { ID = 2, Name = "Narendra2", Salary = 21111, Experience = 10, Age = 38 };
Employee emp3 = new Employee() { ID = 3, Name = "Narendra3", Salary = 31111, Experience = 4, Age = 33 };
Employee emp4 = new Employee() { ID = 3, Name = "Narendra4", Salary = 41111, Experience = 7, Age = 33 };
List<Employee> lstEmployee = new List<Employee>();
lstEmployee.Add(emp1);
lstEmployee.Add(emp2);
lstEmployee.Add(emp3);
lstEmployee.Add(emp4);
var eemmppss=lstEmployee.Select(cc=>new {cc.ID,cc.Age}).Distinct();
참고 URL : https://stackoverflow.com/questions/10719928/how-to-use-linq-distinct-with-multiple-fields
'Nice programing' 카테고리의 다른 글
MVVM 광기 : 명령 (0) | 2020.11.28 |
---|---|
Spring 3 MVC 애플리케이션을위한 maven 2 archetype이 있습니까? (0) | 2020.11.28 |
계산 된 열을 사용하여 동일한보기에서 다른 열을 계산하는 방법 (0) | 2020.11.28 |
Xcode가 일치하는 서명 자산을 찾거나 생성하려고했으나 실패했습니다. (0) | 2020.11.28 |
항목을 삽입하거나 정렬 된 목록에 추가 한 후 목록을 정렬하는 것이 더 빠릅니까? (0) | 2020.11.28 |