【问题标题】:C# 3.0: Need to return duplicates from a List<>C# 3.0:需要从 List<> 返回重复项
【发布时间】:2009-01-29 22:06:51
【问题描述】:

我在 C# 中有一个对象列表,我需要一种方法来返回那些在列表中被视为重复的对象。我不需要 Distinct 结果集,我需要我将从存储库中删除的那些项目的列表。

为了这个例子,假设我有一个“汽车”类型的列表,我需要知道哪些汽车与列表中的其他汽车颜色相同。以下是列表中的汽车及其颜色属性:

Car1.Color = Red;

Car2.Color = Blue;

Car3.Color = Green;

Car4.Color = Red;

Car5.Color = Red;

对于此示例,我需要结果(IEnumerable、List 或其他)包含 Car4 和 Car5,因为我想从我的存储库或数据库中删除它们,以便我的存储库中每种颜色只有一辆汽车.任何帮助将不胜感激。

【问题讨论】:

    标签: c# linq lambda iequalitycomparer


    【解决方案1】:

    我昨天无意中编写了这个代码,当时我正试图写一个“通过投影区分”。我包括一个!当我不应该有的时候,但这次恰到好处:

    public static IEnumerable<TSource> DuplicatesBy<TSource, TKey>
        (this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
    {
        HashSet<TKey> seenKeys = new HashSet<TKey>();
        foreach (TSource element in source)
        {
            // Yield it if the key hasn't actually been added - i.e. it
            // was already in the set
            if (!seenKeys.Add(keySelector(element)))
            {
                yield return element;
            }
        }
    }
    

    然后你会调用它:

    var duplicates = cars.DuplicatesBy(car => car.Color);
    

    【讨论】:

    • 非常感谢乔恩的回答。帮助我优化了在列表中查找重复项的方法。
    • Resharper 告诉我重构代码中的 foreach:return source.Where(element => !seenKeys.Add(keySelector(element)));
    • @Koen:Ick - 我敢说它会起作用,但我不喜欢在谓词中包含副作用的想法。 (这也会改变创建哈希集的时间,但这是小事。)
    • 另一件事:返回所有具有相同键但第一个(按循环顺序)的元素(因此,如果您有 3 个重复项,则返回 2 个元素),这对我来说可能看起来很奇怪.要么返回所有重复项,要么只返回一次密钥...
    【解决方案2】:
    var duplicates = from car in cars
                     group car by car.Color into grouped
                     from car in grouped.Skip(1)
                     select car;
    

    这会按颜色对汽车进行分组,然后跳过每个组的第一个结果,将每个组的剩余部分返回到一个序列中。

    如果您对要保留哪一个有特殊要求,例如如果汽车具有Id 属性,并且您希望将汽车保持在最低Id,那么您可以在其中添加一些排序,例如

    var duplicates = from car in cars
                     group car by car.Color into grouped
                     from car in grouped.OrderBy(c => c.Id).Skip(1)
                     select car;
    

    【讨论】:

    • +1 - 我没有想到 Skip(1) 因为提问者只想要重复项。
    【解决方案3】:

    这是一个稍微不同的 Linq 解决方案,我认为它可以让您更清楚地了解您想要做什么:

    var s = from car in cars
        group car by car.Color into g
        where g.Count() == 1
        select g.First();
    

    它只是按颜色对汽车进行分组,丢弃所有具有多个元素的组,然后将其余部分放入返回的 IEnumerable 中。

    【讨论】:

      【解决方案4】:
      IEnumerable<Car> GetDuplicateColors(List<Car> cars)
      {
          return cars.Where(c => cars.Any(c2 => c2.Color == c.Color && cars.IndexOf(c2) < cars.IndexOf(c) ) );
      }    
      

      它基本上意味着“返回列表中具有相同颜色和较小索引的任何汽车的汽车”。

      但不确定性能。我怀疑使用 O(1) 查找重复项的方法(如字典/哈希集方法)对于大型集合可能更快。

      【讨论】:

        【解决方案5】:

        创建一个新的Dictionary&lt;Color, Car&gt; foundColors 和一个List&lt;Car&gt; carsToDelete

        然后你像这样遍历你的原始汽车列表:

        foreach(Car c in listOfCars)
        {
            if (foundColors.containsKey(c.Color))
            {
                carsToDelete.Add(c);
            }
            else
            {
                foundColors.Add(c.Color, c);
            }
        }
        

        然后您可以删除 foundColors 中的每辆汽车。

        您可以通过将“删除记录”逻辑放入if 语句而不是创建新列表来获得轻微的性能提升,但您对问题的措辞表明您需要将它们收集到一个列表中。

        【讨论】:

          【解决方案6】:

          如果没有实际编码,那么像这样的算法怎么样:

          • 遍历您的List&lt;T&gt; 创建Dictionary&lt;T, int&gt;
          • 遍历您的Dictionary&lt;T, int&gt; 删除int 为>1 的条目

          Dictionary 中留下的任何内容都有重复项。当然,您实际删除的第二部分是可选的。您可以遍历 Dictionary 并寻找 >1 采取行动。

          编辑:好的,我碰上了 Ryan 的,因为他实际上给了你代码。 ;)

          【讨论】:

            【解决方案7】:

            我的回答从追随者受访者(按此顺序)获得灵感:Joe Coehoorn、Greg Beech 和 Jon Skeet。

            我决定提供一个完整的示例,假设(为了实际效率)您有一个静态的汽车颜色列表。我相信以下代码以一种优雅但不一定超高效的方式说明了该问题的完整解决方案。

            #region SearchForNonDistinctMembersInAGenericListSample
            public static string[] carColors = new[]{"Red", "Blue", "Green"}; 
            public static string[] carStyles = new[]{"Compact", "Sedan", "SUV", "Mini-Van", "Jeep"}; 
            public class Car
            {
                public Car(){}
                public string Color { get; set; }
                public string Style { get; set; }
            }
            public static List<Car> SearchForNonDistinctMembersInAList()
            {
                // pass in cars normally, but declare here for brevity
                var cars = new List<Car>(5) { new Car(){Color=carColors[0], Style=carStyles[0]}, 
                                                  new Car(){Color=carColors[1],Style=carStyles[1]},
                                                  new Car(){Color=carColors[0],Style=carStyles[2]}, 
                                                  new Car(){Color=carColors[2],Style=carStyles[3]}, 
                                                  new Car(){Color=carColors[0],Style=carStyles[4]}};
                List<Car> carDupes = new List<Car>();
            
                for (int i = 0; i < carColors.Length; i++)
                {
                    Func<Car,bool> dupeMatcher = c => c.Color == carColors[i];
            
                    int count = cars.Count<Car>(dupeMatcher);
            
                    if (count > 1) // we have duplicates
                    {
                        foreach (Car dupe in cars.Where<Car>(dupeMatcher).Skip<Car>(1))
                        {
                            carDupes.Add(dupe);
                        }
                    }
                }
                return carDupes;
            }
            #endregion
            

            我稍后会回到这里并将这个解决方案与它的所有三个灵感进行比较,只是为了对比风格。挺有意思的。

            【讨论】:

              【解决方案8】:

              public static IQueryable Duplicates(this IEnumerable source) where TSource : IComparable {

              if (source == null)   
                   throw new ArgumentNullException("source");   
               return source.Where(x => source.Count(y=>y.Equals(x)) > 1).AsQueryable<TSource>();   
              

              }

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2013-05-18
                • 1970-01-01
                • 1970-01-01
                • 2012-09-28
                • 1970-01-01
                • 2010-12-06
                • 1970-01-01
                相关资源
                最近更新 更多