如何使用 LINQ 从列表中获取重复项?

我的 List<string>是这样的:

List<String> list = new List<String>{"6","1","2","4","6","5","1"};

我需要把列表中的重复项目放到一个新的列表中。现在我使用一个嵌套的 for循环来完成这项工作。

得到的 list将包含 {"6","1"}

是否有任何想法做到这一点使用 LINQLambda 表达式

195544 次浏览
var duplicates = lst.GroupBy(s => s)
.SelectMany(grp => grp.Skip(1));

注意,这将返回所有重复项,因此如果您只想知道源列表中的哪些项是重复的,那么您可以将 Distinct应用到结果序列或使用 马克 · 拜尔斯给出的解决方案

这里有一个方法:

List<String> duplicates = lst.GroupBy(x => x)
.Where(g => g.Count() > 1)
.Select(g => g.Key)
.ToList();

GroupBy将相同的元素组合在一起,Where过滤掉那些只出现一次的元素,只留下重复的元素。

希望这个能帮上忙

int[] listOfItems = new[] { 4, 2, 3, 1, 6, 4, 3 };


var duplicates = listOfItems
.GroupBy(i => i)
.Where(g => g.Count() > 1)
.Select(g => g.Key);


foreach (var d in duplicates)
Console.WriteLine(d);

还有一个选择:

var list = new List<string> { "6", "1", "2", "4", "6", "5", "1" };


var set = new HashSet<string>();
var duplicates = list.Where(x => !set.Add(x));
  List<String> list = new List<String> { "6", "1", "2", "4", "6", "5", "1" };


var q = from s in list
group s by s into g
where g.Count() > 1
select g.First();


foreach (var item in q)
{
Console.WriteLine(item);


}

我根据@Lee 对 OP 的响应编写了这个扩展方法。注意,使用了一个默认参数(需要 C # 4.0)。然而,C # 3.0中的一个重载方法调用就足够了。

/// <summary>
/// Method that returns all the duplicates (distinct) in the collection.
/// </summary>
/// <typeparam name="T">The type of the collection.</typeparam>
/// <param name="source">The source collection to detect for duplicates</param>
/// <param name="distinct">Specify <b>true</b> to only return distinct elements.</param>
/// <returns>A distinct list of duplicates found in the source collection.</returns>
/// <remarks>This is an extension method to IEnumerable&lt;T&gt;</remarks>
public static IEnumerable<T> Duplicates<T>
(this IEnumerable<T> source, bool distinct = true)
{
if (source == null)
{
throw new ArgumentNullException("source");
}


// select the elements that are repeated
IEnumerable<T> result = source.GroupBy(a => a).SelectMany(a => a.Skip(1));


// distinct?
if (distinct == true)
{
// deferred execution helps us here
result = result.Distinct();
}


return result;
}

我试图用一个对象列表来解决同样的问题,但遇到了问题,因为我试图将组列表重新打包到原始列表中。因此,我提出了通过组进行循环来使用重复的项目重新打包原始 List。

public List<MediaFileInfo> GetDuplicatePictures()
{
List<MediaFileInfo> dupes = new List<MediaFileInfo>();
var grpDupes = from f in _fileRepo
group f by f.Length into grps
where grps.Count() >1
select grps;
foreach (var item in grpDupes)
{
foreach (var thing in item)
{
dupes.Add(thing);
}
}
return dupes;
}

我知道这不是最初问题的答案,但你可能会发现自己在这里与这个问题。

如果希望在结果中包含所有重复项,可以使用以下方法。

var duplicates = list
.GroupBy( x => x )               // group matching items
.Where( g => g.Skip(1).Any() )   // where the group contains more than one item
.SelectMany( g => g );           // re-expand the groups with more than one item

在我的情况下,我需要所有的副本,以便我可以标记他们在 UI 中作为错误。

到目前为止,所有提到的解决方案都执行 GroupBy。即使我只需要第一个副本集合的所有元素都至少枚举一次。

下面的扩展函数在找到副本后立即停止枚举。如果请求下一个副本,它将继续。

和往常一样,LINQ 有两个版本,一个带有 IEqualityComparer,另一个没有。

public static IEnumerable<TSource> ExtractDuplicates(this IEnumerable<TSource> source)
{
return source.ExtractDuplicates(null);
}
public static IEnumerable<TSource> ExtractDuplicates(this IEnumerable<TSource source,
IEqualityComparer<TSource> comparer);
{
if (source == null) throw new ArgumentNullException(nameof(source));
if (comparer == null)
comparer = EqualityCompare<TSource>.Default;


HashSet<TSource> foundElements = new HashSet<TSource>(comparer);
foreach (TSource sourceItem in source)
{
if (!foundElements.Contains(sourceItem))
{   // we've not seen this sourceItem before. Add to the foundElements
foundElements.Add(sourceItem);
}
else
{   // we've seen this item before. It is a duplicate!
yield return sourceItem;
}
}
}

用法:

IEnumerable<MyClass> myObjects = ...


// check if has duplicates:
bool hasDuplicates = myObjects.ExtractDuplicates().Any();


// or find the first three duplicates:
IEnumerable<MyClass> first3Duplicates = myObjects.ExtractDuplicates().Take(3)


// or find the first 5 duplicates that have a Name = "MyName"
IEnumerable<MyClass> myNameDuplicates = myObjects.ExtractDuplicates()
.Where(duplicate => duplicate.Name == "MyName")
.Take(5);

对于所有这些 linq 语句,只在找到请求的项之前解析集合。不解释序列的其余部分。

恕我直言,这是一个值得考虑的提高效率的方法。