使用LINQ创建临时工作变量
本文关键字:工作 变量 LINQ 创建 使用 | 更新日期: 2023-09-27 18:01:36
问题:我有一个int列表,我想得到存在两次或更多次的数字。
List<int> firstList = new List<int> { 1, 1, 3 };
预期结果:
{ 1 }
这可以通过LINQ轻松完成。。例如,这个
var result = firstList.Where(c => firstList.Count(d => c == d) > 1).Distinct();
问题是,这会进行不止一次的迭代。对于循环的法线,我们可以达到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中声明我的"新哈希集"。我在想firstList.Aggregate((c, d = new HashSet<int>) =>
之类的东西。。
一个简单的方法是:
var repeated = list.GroupBy(x => x)
.Where(g => g.Count() > 1)
.Select(g => g.Key);
它只会迭代一次——它的效率略低于手工制作的解决方案,但应该非常合理——而且非常简单。
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();
而且,将它封装到一个新的扩展中通常是值得的。请注意,所有这些代码与您和其他人之前提供的代码几乎相同。它只是被"很好地包装"到"变量隐藏器"或"扩展"中。
Edit:是的,这是真的,所有带有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();
但这主要是.dispinct((无论如何都会做的事情。
从技术上讲,你可以这样做(只是你正在做的事情的一种更短的方式(:
var hashSet = new HashSet<int>();
var filtered = firstList
.Where(x =>
{
if (hashSet.Contains(x)) return true;
hashSet.Add(x);
return false;
});
但我认为最好避免这种副作用,只使用上面Jon Skeet的方法(安全地假设它在上面:(
编辑:
根据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)