【问题标题】:How do I perform a DISTINCT query using LINQ (.NET 4)?如何使用 LINQ (.NET 4) 执行 DISTINCT 查询?
【发布时间】:2011-08-19 12:05:50
【问题描述】:

我有一个对象集并想对集合执行 DISTINCT。返回的对象每个都有大约 10 个属性。我想使用这些属性,所以投影真的不是一种选择。在 10 个属性中,我只希望将 DISTINCT 应​​用于其中两个字段(DistrictId 和 ContactId)。我该怎么做?

【问题讨论】:

    标签: c# linq entity-framework .net-4.0


    【解决方案1】:

    我们可以定义一个扩展方法来对 T 的 IEnumerable 执行 DistinctBy 操作,如下所示:

    public static class EnumerableExtensions
            {
    
                /// <summary>
                /// Returns a ienumerable which is distinct by a given property key selector. If a custom equality 
                /// comparer is to be used, pass this in as the comparer. By setting the comparer default to null,
                /// the default comparer is used. 
                /// </summary>
                /// <typeparam name="T">The item type in the ienumerable</typeparam>
                /// <typeparam name="TKey">The type of the key selector (property to disinct elements by)</typeparam>
                /// <param name="coll">The source ienumerable</param>
                /// <param name="keySelector">The key selector, use a member expression in a lambda expression</param>
                /// <param name="comparer">Custom comparer to use, pass in null here to specify that default comparer is used,
                /// however, this is default set to null and not required parameter</param>
                /// <returns></returns>
                public static IEnumerable<T> DistinctBy<T, TKey>(this IEnumerable<T> coll, Func<T, TKey> keySelector,
                    IEqualityComparer<TKey> comparer = null)
                {
                    if (coll == null)
                        throw new ArgumentNullException("coll");
                    if (keySelector == null)
                        throw new ArgumentNullException("keySelector");
    
                    var result = coll.GroupBy(keySelector, comparer).Select(g => g.First()).ToList();
                    return new List<T>(result).AsEnumerable();
                }
    
            }
    

    然后可以在一个简单的控制台应用程序中测试 DistinctBy 运算符,我们希望获得不同的 (Model, Color) 元组,即我们只需要 Model+Color 组合的汽车:

    类程序 {

        static void Main(string[] args)
        {
    
            var cars = new []
            {
                new Car {Model = "Audi", Make = "A4", Color = "Black"},
                new Car {Model = "Audi", Make = "A8", Color = "Red"},
                new Car {Model = "Audi", Make = "TT", Color = "Black"},
                new Car {Model = "Volvo", Make = "XC90", Color = "Black"},
                new Car {Model = "Volvo", Make = "S90", Color = "Black"},
                new Car {Model = "Ferrari", Make = "F500", Color = "Yellow"},
                new Car {Model = "Ferrari", Make = "F500", Color = "Red"},
                new Car {Model = "Lada", Make = "Limousine", Color = "Rusty"}
            };
    
            var groupedCars = cars.DistinctBy(c => new {c.Model, c.Color});
    
    
            foreach (var gc in groupedCars)
            {
                Console.WriteLine(gc.ToString()); 
            }
    
            Console.WriteLine("Press any key to continue ...");
            Console.ReadKey(); 
        }
    
    
    
    
        // Define other methods and classes here
    
    }
    

    那么输出是:

    Model: Audi, Make: A4, Color: Black
    Model: Audi, Make: A8, Color: Red
    Model: Volvo, Make: XC90, Color: Black
    Model: Ferrari, Make: F500, Color: Yellow
    Model: Ferrari, Make: F500, Color: Red
    Model: Lada, Make: Limousine, Color: Rusty
    Press any key to continue ...
    

    我们没有得到“Audi TT Black”项目,因为我们已经得到了一辆黑色奥迪。我们没有得到“Volvo S90 Black”,因为我们已经得到了黑色沃尔沃。我们得到了法拉利 F500,因为它们有不同的颜色。可悲的是,我们坚持使用“Lada Limousine Rusty”,因为它是模型和颜色的唯一组合。

    【讨论】:

      【解决方案2】:

      您可以尝试构造一个匿名类型来处理这种情况。 假设你的类有 10 个属性是 Element

      public class Element
          {
              public int FirstProp { get; set; }
              public int SecondProp { get; set; }
      
             //others 8 cool properties
          }
      

      使用 linq 中的扩展方法提取所需内容的查询:

      IList<Element> element = new List<Element>();
      
      
                  var result = new { P1 = element
                                      .Select(X => X.FirstProp).Distinct()
                                  ,
                                     P2 = element
                                          .Select(X => X.SecondProp).Distinct()
                                  ,
                                      element
                                     // do projections here over others 8 properties
      
      
                  };
      

      【讨论】:

        【解决方案3】:

        由于您只需要在 DistrictId 和 ContactId 组合方面不同的元素,因此您可以使用GroupBy,然后决定如何处理重复项。在这种情况下,每个组都代表属于一个不同组合的项目。

        var results = context.MyCollection
                             .GroupBy( x=> new { x.DistrictId, x.ContactId })
                             .Select(...)
        

        【讨论】:

        • 如果您只希望每个组中的一个使用Select( g =&gt; g.First());
        【解决方案4】:

        您需要自己编写或获取DistinctBy 方法的实现,该方法允许您在保留序列类型的同时对序列内容的投影信息进行区分。

        MoreLINQ 提供an implementation of this method

        【讨论】:

        • ... 并使用 Tuple.Create(x.DistrictId, x.ContactId) 作为键选择器
        猜你喜欢
        • 2016-01-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-01-01
        • 1970-01-01
        相关资源
        最近更新 更多