LINQ:不同的值


136

我从XML中设置了以下项目:

id           category

5            1
5            3
5            4
5            3
5            3

我需要这些项目的明确清单:

5            1
5            3
5            4

如何在LINQ中区分类别和ID?

Answers:


221

您是否想通过多个领域来与众不同?如果是这样,只需使用匿名类型和Distinct运算符,就可以了:

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

如果您试图获取一组不同的“更大”类型的值,但仅查看不透明度方面的某些属性子集,则可能需要DistinctByMoreLINQ中实现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作为比较器传入,它将使用默认的比较器作为键类型。)


哦,所以用“更大的类型”,您可能意味着我仍然想要结果中的所有属性,即使我只想比较几个属性以确定不同性?
红豌豆

@TheRedPea:是的,完全正确。
乔恩·斯基特


27

除了Jon Skeet的答案外,您还可以使用group by表达式来获取每个组迭代的w /计数中的唯一组:

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

13

对于任何还在寻找的人;这是实现自定义Lambda比较器的另一种方法。

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创建一个扩展,该扩展可以接受lambda的

   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使用哈希集存储它已经产生的元素。始终返回相同的哈希码意味着每次都会检查每个先前返回的元素。更健壮的哈希码将加快处理速度,因为它只能与同一哈希存储桶中的元素进行比较。零是一个合理的默认值,但可能值得为哈希码支持第二个lambda。
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());

这将为您提供按选择匹配组的第一个整体元素,非常类似于Jon Skeets使用DistinctBy的第二个示例,但没有实现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()返回集差的过程中想到了这一点

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.