C#3.0:需要从List 返回重复项

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

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

Car1.Color = Red; Car2.Color = Blue; Car3.Color = Green; Car4.Color = Red; Car5.Color = Red; 

对于这个例子,我需要结果(IEnumerable ,List 或其他)来包含Car4和Car5,因为我想从我的存储库或数据库中删除这些,这样我的存储库中每种颜色只有一个汽车。 任何帮助,将不胜感激。

昨天,当我试图写一个“与投影截然不同”时,我无意中编码了这个。 我加了一个! 当我不应该,但这次是正确的:

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

然后你用它来调用它:

 var duplicates = cars.DuplicatesBy(car => car.Color); 
 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; 

这是一个略有不同的Linq解决方案,我认为这使您更明显地尝试做什么:

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

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

 IEnumerable GetDuplicateColors(List cars) { return cars.Where(c => cars.Any(c2 => c2.Color == c.Color && cars.IndexOf(c2) < cars.IndexOf(c) ) ); } 

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

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

创建一个新的Dictionary foundColors和一个List carsToDelete

然后你迭代你原来的汽车列表,如下所示:

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

然后你可以删除在foundColors中的每辆车。

通过在if语句中添加“删除记录”逻辑而不是创建新列表,可以获得轻微的性能提升,但是您提出问题的方式表明您需要在List中收集它们。

没有实际编码,算法如下:

  • 迭代List创建Dictionary
  • 迭代您的Dictionary删除int > 1的条目

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

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

我的答案从受访者的受访者那里获得灵感(按此顺序):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 SearchForNonDistinctMembersInAList() { // pass in cars normally, but declare here for brevity var cars = new List(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 carDupes = new List(); for (int i = 0; i < carColors.Length; i++) { Func dupeMatcher = c => c.Color == carColors[i]; int count = cars.Count(dupeMatcher); if (count > 1) // we have duplicates { foreach (Car dupe in cars.Where(dupeMatcher).Skip(1)) { carDupes.Add(dupe); } } } return carDupes; } #endregion 

我稍后会回到这里,将这个解决方案与其所有三个灵感进行比较,以对比这些风格。 这很有趣。

public static IQueryable Duplicates(此IEnumerable源),其中TSource:IComparable {

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

}