我有一个List<> C#中的对象,我需要一种方法来返回列表中被认为是重复的对象。我不需要Distinct结果集,我需要一个我将从我的存储库中删除的项目列表。
为了这个例子,假设我有一个“汽车”类型的列表,我需要知道这些汽车中的哪一个与列表中的另一辆汽车颜色相同。以下是列表中的汽车及其颜色属性:
Car1.Color = Red;
Car2.Color = Blue;
Car3.Color = Green;
Car4.Color = Red;
Car5.Color = Red;
对于这个例子,我需要结果(IEnumerable<>,List<>或其他)来包含Car4和Car5,因为我想从我的存储库或数据库中删除它们,这样我每个颜色只有一辆车我的存储库。任何帮助将不胜感激。
答案 0 :(得分:29)
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);
答案 1 :(得分:17)
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;
答案 2 :(得分:5)
这是一个略有不同的Linq解决方案,我认为这会使您更加明显地尝试做什么:
var s = from car in cars
group car by car.Color into g
where g.Count() == 1
select g.First();
它只是按颜色对汽车进行分组,抛弃所有具有多个元素的组,然后将其余组件放入返回的IEnumerable中。
答案 3 :(得分:3)
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)查找重复项(如字典/ hashset方法)的方法对于大型集合来说可能更快。
答案 4 :(得分:3)
制作新的Dictionary<Color, Car> foundColors
和List<Car> carsToDelete
然后你遍历原来的汽车列表,如下:
foreach(Car c in listOfCars)
{
if (foundColors.containsKey(c.Color))
{
carsToDelete.Add(c);
}
else
{
foundColors.Add(c.Color, c);
}
}
然后你可以删除在foundColors中的每辆车。
通过在if
语句中添加“删除记录”逻辑而不是创建新列表,您可以获得轻微的性能提升,但您提出问题的方式表明您需要在列表中收集它们。
答案 5 :(得分:0)
如果没有实际编码,那么算法如何:
List<T>
创建Dictionary<T, int>
Dictionary<T, int>
删除int
为&gt; 1 Dictionary
中剩下的任何内容都有重复内容。当然,实际删除的第二部分是可选的。您可以遍历Dictionary
并查找&gt; 1来采取行动。
答案 6 :(得分:0)
我的答案从受访者中获得灵感(按此顺序):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
我稍后会回到这里,将这个解决方案与其所有三个灵感进行比较,只是为了对比这些风格。这很有趣。
答案 7 :(得分:0)
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<TSource>();
}