问题: 我有一个整数列表,我想得到两次或更多次存在的数字。
List<int> firstList = new List<int> { 1, 1, 3 };
预期结果:
{ 1 }
这可以通过LINQ轻松完成..例如这个
var result = firstList.Where(c => firstList.Count(d => c == d) > 1).Distinct();
问题是这不止一次迭代。使用正常的for循环,我们可以达到O(N)的时间..
List<int> result = new List<int>();
HashSet<int> doubles = new HashSet<int>();
foreach (var v in firstList)
{
if (!doubles.Contains(v))
doubles.Add(v);
else
result.Add(v);
}
我们想用linq aswel ...
HashSet<int> doubles = new HashSet<int>();
var result = firstList.Where((c) => doubles.Contains(c) ? true : !doubles.Add(c)).ToList();
这是我能想到的唯一方法..
问题:
有什么方法可以在LINQ中声明我的“新HashSet”。我在想firstList.Aggregate((c, d = new HashSet<int>) =>
..
答案 0 :(得分:3)
一种简单的方法是:
var repeated = list.GroupBy(x => x)
.Where(g => g.Count() > 1)
.Select(g => g.Key);
这只会迭代一次 - 它的效率会比手工制作的解决方案略低,但应该非常合理 - 而且非常简单。
答案 1 :(得分:2)
let
关键字允许你引入一些东西,但这主要是由编译器以与Eren帖子中看到的var hashset
类似的方式重写的。正如您无法“隐藏”源firstList
一样,您通常无法隐藏其他支持变量。至少,以一种理智的方式。
存在疯狂的方式。通过疯狂,我的意思是更不可读和模糊。
例如,让我们用变量隐藏重写Eren的例子:
var firstList = new[] { 1, 1, 3 };
var result = Enumerable.Repeat(new { list = firstList, hash = new HashSet<int>() }, 1)
.Select(anon => anon.list.Where(x => !anon.hash.Add(x)))
.SelectMany(_ => _);
但它值得吗?
另外,请不要将自己局限于标准LINQ运算符。您可以轻松介绍自己的:
public static class MyOps
{
public static IEnumerable<T> FindDuplicates<T>(this IEnumerable<T> input)
{
var hashSet = new HashSet<T>();
foreach (var item in input)
if (!hashSet.Add(item))
yield return item;
}
}
var firstList = new[] { 1, 1, 3 };
var result1 = firstList.FindDuplicates();
并且,通常值得花费很少的努力将其包装到新的扩展中。请注意,所有这些代码几乎与您和其他人提供的代码完全相同。它只是“很好地包装”成“变量 - 隐藏”或“扩展”。
编辑:是的,这是真的,所有带有hashset的示例都将返回所有重复项。您可以通过两个哈希集来实现它,而不是区分它们:一个用于重复检查,另一个用于过滤重复结果。
public static class MyOps
{
public static IEnumerable<T> FindDuplicates<T>(this IEnumerable<T> input)
{
var hashSet1 = new HashSet<T>();
var hashSet2 = new HashSet<T>();
foreach (var item in input)
if (!hashSet1.Add(item)) // note the negation
if (hashSet2.Add(item)) // note NO negation
yield return item;
}
}
var firstList = new[] { 1, 1, 3 };
var result1 = firstList.FindDuplicates();
但这主要是.distinct()无论如何都会做的。
答案 2 :(得分:1)
从技术上讲,你可以做到这一点(只是你所做的更短的方式):
var hashSet = new HashSet<int>();
var filtered = firstList
.Where(x =>
{
if (hashSet.Contains(x)) return true;
hashSet.Add(x);
return false;
});
但我认为最好避免这种副作用,只使用上面的Jon Skeet方法(安全地假设它在上面:) :)
修改强>
根据Jon Jon Skeet的评论,这甚至可以缩短为:
var hashSet = new HashSet<int>();
var filtered = firstList.Where(x => !hashSet.Add(x));
请注意,使用此功能需要注意一次。例如:
var list1 = filtered.ToList(); // correct result
var list2 = filtered.ToList(); // incorrect result (returns all numbers)