LINQ : 고유 한 값


136

XML에서 다음 항목 세트가 있습니다.

id           category

5            1
5            3
5            4
5            3
5            3

이 항목들에 대한 명확한 목록이 필요합니다.

5            1
5            3
5            4

LINQ에서 카테고리와 ID를 어떻게 구별 할 수 있습니까?

답변:


221

하나 이상의 필드로 구별하려고합니까? 그렇다면 익명 유형과 고유 연산자를 사용하십시오.

var query = doc.Elements("whatever")
               .Select(element => new {
                             id = (int) element.Attribute("id"),
                             category = (int) element.Attribute("cat") })
               .Distinct();

"더 큰"유형의 고유 한 값 세트를 가져 오려고 하지만 고유성 측면에 대한 특성의 일부 서브 세트 만보고자한다면 MoreLINQ 에서 DistinctBy구현 된 것이 좋습니다DistinctBy.cs .

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

null비교 자로 전달 하면 키 유형에 기본 비교기가 사용됩니다.


"더 큰 유형"이란 구별을 결정하기 위해 몇 가지 속성 만 비교하려는 경우에도 결과의 모든 속성을 원한다는 것을 의미 할 수 있습니다.
The Red Pea

@TheRedPea : 그렇습니다.
Jon Skeet


27

Jon Skeet의 답변 외에도 그룹을 표현식별로 사용하여 각 그룹 반복에 대한 개수와 함께 고유 한 그룹을 얻을 수 있습니다.

var query = from e in doc.Elements("whatever")
            group e by new { id = e.Key, val = e.Value } into g
            select new { id = g.Key.id, val = g.Key.val, count = g.Count() };

4
당신은 "Jon Skeet의 답변에 덧붙여"라고 썼습니다. 그런 일이 가능한지 모르겠습니다. ;)
Yehuda Makarov '12

13

여전히 찾고있는 사람이라면 다음은 커스텀 람다 비교기를 구현하는 또 다른 방법입니다.

public class LambdaComparer<T> : IEqualityComparer<T>
    {
        private readonly Func<T, T, bool> _expression;

        public LambdaComparer(Func<T, T, bool> lambda)
        {
            _expression = lambda;
        }

        public bool Equals(T x, T y)
        {
            return _expression(x, y);
        }

        public int GetHashCode(T obj)
        {
            /*
             If you just return 0 for the hash the Equals comparer will kick in. 
             The underlying evaluation checks the hash and then short circuits the evaluation if it is false.
             Otherwise, it checks the Equals. If you force the hash to be true (by assuming 0 for both objects), 
             you will always fall through to the Equals check which is what we are always going for.
            */
            return 0;
        }
    }

그런 다음 람다에 걸릴 수있는 linq Distinct에 대한 확장을 만들 수 있습니다

   public static IEnumerable<T> Distinct<T>(this IEnumerable<T> list,  Func<T, T, bool> lambda)
        {
            return list.Distinct(new LambdaComparer<T>(lambda));
        }  

용법:

var availableItems = list.Distinct((p, p1) => p.Id== p1.Id);

참조 소스를 살펴보면 Distinct는 해시 세트를 사용하여 이미 생성 된 요소를 저장합니다. 항상 동일한 해시 코드를 반환한다는 것은 이전에 반환 된 모든 요소가 매번 검사됨을 의미합니다. 보다 강력한 해시 코드는 동일한 해시 버킷의 요소와 만 비교하기 때문에 속도가 빨라집니다. 0은 합리적인 기본값이지만 해시 코드에 대한 두 번째 람다를 지원하는 것이 좋습니다.
Darryl

좋은 지적! 시간이되면 편집을해볼 것입니다. 지금이 도메인에서 일하고 있다면 자유롭게 편집하십시오.
Ricky G

8

답변에 약간 늦었지만 그룹화하려는 값뿐만 아니라 전체 요소를 원할 경우이 작업을 수행 할 수 있습니다.

var query = doc.Elements("whatever")
               .GroupBy(element => new {
                             id = (int) element.Attribute("id"),
                             category = (int) element.Attribute("cat") })
               .Select(e => e.First());

이렇게하면 DistinctBy를 사용하는 Jon Skeets의 두 번째 예제와 유사하지만 IEqualityComparer 비교기를 구현하지 않고도 선택에 따라 그룹과 일치하는 첫 번째 전체 요소가 제공됩니다. DistinctBy가 더 빠를 가능성이 높지만 성능에 문제가없는 경우 위의 솔루션에는 더 적은 코드가 포함됩니다.


4
// First Get DataTable as dt
// DataRowComparer Compare columns numbers in each row & data in each row

IEnumerable<DataRow> Distinct = dt.AsEnumerable().Distinct(DataRowComparer.Default);

foreach (DataRow row in Distinct)
{
    Console.WriteLine("{0,-15} {1,-15}",
        row.Field<int>(0),
        row.Field<string>(1)); 
}

0

우리는 모든 요소를 ​​정확히 한 번만 갖는 것에 대해 이야기하고 있기 때문에 "집합"이 더 의미가 있습니다.

클래스와 IEqualityComparer가 구현 된 예 :

 public class Product
    {
        public int Id { get; set; }
        public string Name { get; set; }

        public Product(int x, string y)
        {
            Id = x;
            Name = y;
        }
    }

    public class ProductCompare : IEqualityComparer<Product>
    {
        public bool Equals(Product x, Product y)
        {  //Check whether the compared objects reference the same data.
            if (Object.ReferenceEquals(x, y)) return true;

            //Check whether any of the compared objects is null.
            if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y, null))
                return false;

            //Check whether the products' properties are equal.
            return x.Id == y.Id && x.Name == y.Name;
        }
        public int GetHashCode(Product product)
        {
            //Check whether the object is null
            if (Object.ReferenceEquals(product, null)) return 0;

            //Get hash code for the Name field if it is not null.
            int hashProductName = product.Name == null ? 0 : product.Name.GetHashCode();

            //Get hash code for the Code field.
            int hashProductCode = product.Id.GetHashCode();

            //Calculate the hash code for the product.
            return hashProductName ^ hashProductCode;
        }
    }

지금

List<Product> originalList = new List<Product> {new Product(1, "ad"), new Product(1, "ad")};
var setList = new HashSet<Product>(originalList, new ProductCompare()).ToList();

setList 독특한 요소를 가질 것이다

나는 .Except()세트 차이를 반환하는 것을 처리하면서 이것을 생각했다.

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