C#3.0:需要返回列表中的副本<&燃气轮机;

C#3.0:需要返回列表中的副本<&燃气轮机;,c#,linq,lambda,iequalitycomparer,C#,Linq,Lambda,Iequalitycomparer,我有一个C#中的对象列表,我需要一种方法返回那些在列表中被视为重复的对象。我不需要独特的结果集,我需要从存储库中删除的那些项目的列表 在本例中,假设我有一个“汽车”类型的列表,我需要知道这些汽车中的哪些与列表中的另一辆颜色相同。以下是列表中的汽车及其颜色属性: Car1.Color = Red; Car2.Color = Blue; Car3.Color = Green; Car4.Color = Red; Car5.Color = Red; 对于本例,我需要结果(IEnumerabl

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

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

Car1.Color = Red;

Car2.Color = Blue;

Car3.Color = Green;

Car4.Color = Red;

Car5.Color = Red;
对于本例,我需要结果(IEnumerable、List或其他任何内容)包含Car4和Car5,因为我想从我的存储库或db中删除它们,这样我的存储库中每种颜色只有一辆车。任何帮助都将不胜感激。

IEnumerable GetDuplicateColor(列出汽车)
IEnumerable<Car> GetDuplicateColors(List<Car> cars)
{
    return cars.Where(c => cars.Any(c2 => c2.Color == c.Color && cars.IndexOf(c2) < cars.IndexOf(c) ) );
}    
{ 返回cars.Where(c=>cars.Any(c2=>c2.Color==c.Color&&cars.IndexOf(c2)
它的基本意思是“如果列表中有相同颜色和较小索引的汽车,则返回汽车”


不过,我对性能不太确定。我怀疑使用O(1)查找重复项的方法(如dictionary/hashset方法)可以更快地查找大型集合。

创建新的
dictionary foundColors
列表carsToDelete

然后你反复浏览你的原始汽车列表,如下所示:

foreach(Car c in listOfCars)
{
    if (foundColors.containsKey(c.Color))
    {
        carsToDelete.Add(c);
    }
    else
    {
        foundColors.Add(c.Color, c);
    }
}
然后,您可以删除FoundColor中的每辆车


通过在
if
语句中添加“delete record”逻辑,而不是创建一个新的列表,您可以获得一个小的性能提升,但是您对问题的措辞表明,您需要将它们收集到一个列表中

如果不进行实际编码,那么像这样的算法如何:

  • 反复浏览
    列表
    创建
    词典
  • 遍历
    词典
    删除
    int
    大于1的条目
字典中的任何内容都有重复项。当然,您实际删除的第二部分是可选的。您只需遍历
字典
,并查找要采取行动的>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;
        }
    }
}
这将按颜色对汽车进行分组,然后跳过每组的第一个结果,将每组的剩余结果平铺成单个序列

如果您对要保留哪一辆车有特殊要求,例如,如果该车具有
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;

我的答案(按此顺序)来自以下受访者:乔·科霍恩、格雷格·比奇和乔恩·斯基特

我决定提供一个完整的例子,假设你有一个汽车颜色的静态列表。我相信下面的代码以一种优雅的方式(虽然不一定是超高效的)说明了问题的完整解决方案

#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
#区域搜索代理列表样本中的非唯一成员
公共静态字符串[]carColors=new[]{“红色”、“蓝色”、“绿色”};
公共静态字符串[]carStyles=new[]{“紧凑型”、“轿车”、“SUV”、“迷你面包车”、“吉普车”};
公车
{
公共汽车(){}
公共字符串颜色{get;set;}
公共字符串样式{get;set;}
}
public static List SearchForNonDistinctMembersInAList()
{
//正常情况下可以上车,但为了简洁起见,请在此申报
var cars=new List(5){new Car(){Color=carColors[0],Style=carStyles[0]},
新车(){Color=carColors[1],Style=carStyles[1]},
新车(){Color=carColors[0],Style=carStyles[2]},
新车(){Color=carColors[2],Style=carStyles[3]},
新车(){Color=carColors[0],Style=carStyles[4]};
列表卡片=新列表();
for(int i=0;ic.Color==carColors[i];
int count=cars.count(双匹配器);
如果(计数>1)//我们有重复项
{
foreach(汽车中的汽车dupe.Where(dupeMatcher.Skip(1))
{
卡片。添加(复制);
}
}
}
退货卡;
}
#端区

稍后我会回到这里,将这个解决方案与它的三个灵感进行比较,只是为了对比一下它们的风格。这很有趣。

这里有一个稍微不同的Linq解决方案,我认为它可以让您更清楚地了解您要做的事情:

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

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

公共静态IQueryable副本(此IEnumerable源),其中TSource:IComparable {

if(source==null)
抛出新的ArgumentNullException(“源”);
返回source.Where(x=>source.Count(y=>y.Equals(x))>1.AsQueryable();

}

+1-我没有想到跳过(1),因为提问者只想要副本。非常感谢你的回答Jon。帮助我优化在列表中查找重复项的方法。Resharper告诉我使用以下命令重构代码中的foreach:return source.Where(element=>!seenKeys.Add(keySelector(element))@科恩:嗯,我敢说这会奏效,但我不喜欢在谓词中包含副作用的想法。(这也会改变hashset创建的时间,但这是一件小事。)另一件事:返回具有相同键但第一个(循环顺序)的所有元素(因此,如果有3个重复项,则返回2个元素),这在我看来可能是一种奇怪的行为。要么返回所有副本,要么只返回一次密钥。。。
#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
var s = from car in cars
    group car by car.Color into g
    where g.Count() == 1
    select g.First();
if (source == null)   
     throw new ArgumentNullException("source");   
 return source.Where(x => source.Count(y=>y.Equals(x)) > 1).AsQueryable<TSource>();