LINQ:不同的值

Posted

技术标签:

【中文标题】LINQ:不同的值【英文标题】:LINQ: Distinct values 【发布时间】:2010-11-03 03:22:24 【问题描述】:

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

id           category

5            1
5            3
5            4
5            3
5            3

我需要这些项目的不同列表:

5            1
5            3
5            4

如何在 LINQ 中区分 Category AND Id?

【问题讨论】:

【参考方案1】:

您是否试图通过多个领域来区分?如果是这样,只需使用匿名类型和 Distinct 运算符就可以了:

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

如果您试图获得一组不同的“更大”类型的值,但只查看一些属性的子集以了解不同方面,您可能希望 DistinctBy 实现在 MoreLINQ 中的 @987654322 @:

 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:是的,没错。【参考方案2】:

只需将Distinct() 与您自己的比较器一起使用。

http://msdn.microsoft.com/en-us/library/bb338049.aspx

【讨论】:

【参考方案3】:

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

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() ;

【讨论】:

你写了“除了 Jon Skeet 的回答”......我不知道这样的事情是否可能。 ;)【参考方案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)); 

【讨论】:

【参考方案5】:

我回答得有点晚了,但如果你想要整个元素,而不仅仅是你想要分组的值,你可能想要这样做:

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 很可能会更快,但如果性能不是问题,上述解决方案将涉及更少的代码。

【讨论】:

【参考方案6】:

对于任何仍在寻找的人;这是实现自定义 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。 好点!有时间我会尝试编辑,如果您目前在此域中工作,请随时编辑【参考方案7】:

由于我们谈论的是每个元素都只有一次,所以“集合”对我来说更有意义。

实现类和 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() 时想到了这一点

【讨论】:

以上是关于LINQ:不同的值的主要内容,如果未能解决你的问题,请参考以下文章

使用 LINQ 对不同的值进行排序 [重复]

Linq 查询获取与 sql 查询视图不同的值

如何使用 LINQ to SQL 在计算列上获得不同的值?

在C#中从CSV到Datagridview读取不同的值

linq:在不同类型的条件下左加入 linq

如何使用 C#/LINQ 将 XML 转换为 JSON?