我有一个可能包含重复项的无序枚举,我想删除所有具有重复项的项,并且只保留在原始枚举中只出现一次的项。
示例:A和C被删除,因为它们不止一次出现:
输入{A,C,B,A,C,D,A}
输出{B,D}
快速而肮脏的实施可能是:
IEnumerable<T> Filter(IEnumerable<T> items)
{
items.Where(item => items.Count(x => x.Equals(item)) == 1);
}
显然不是快速或优雅。
下面的示例仍然是二次的(稍快),但需要在输入上调用ToList()。
IEnumerable<T> Filter(IEnumerable<T> items)
{
List<T> src = items.ToList();
for(int i=0; i<src.Count; i++)
{
if (src.IndexOf(src[i], i+1) < 0)
yield return src[i];
}
}
如果你想让它变得相当紧凑和可读(代码明智),你怎么会这样做,同时仍然没有像这些实现一样慢慢的脑死亡?
答案 0 :(得分:6)
LINQ使GroupBy
:
IEnumerable<String> foo = new[]{ "A", "C", "B", "A", "C", "D", "A" };
Ienumerable<String> result = foo.GroupBy (x => x) // A=>3,C=>2,B=>1,D=>1
.Where(x => x.Count() == 1) // B=>1,D=>1
.Select (x => x.Key); // B,D
不确定您需要什么性能,但我倾向于发现GroupBys本身可读。
答案 1 :(得分:1)
您可以在O(N)
时间内完成此操作。
算法:
此解决方案需要两次完整扫描:其中一个输入,第二个结果字典。虽然,它不是LINQ,但实际上可能比LINQ工作得快。
class Program
{
static void Main(string[] args)
{
var input = new[] { "A", "C", "B", "A", "C", "D", "A" };
var result = Filter(input);
Console.WriteLine(result);
}
static IEnumerable<T> Filter<T>(IEnumerable<T> items)
{
var dictionary = new Dictionary<T, int>();
//first scan of the input
foreach (T item in items)
{
if (dictionary.ContainsKey(item))
{
dictionary[item]++;
}
else
{
dictionary[item] = 1;
}
}
//second scan
return from x in dictionary
where x.Value == 1
select x.Key;
}
}
答案 2 :(得分:0)
使用套装怎么样:
IEqualityComparer<T> comparer = EqualityComparer<T>.Default;
HashSet<T> itemsToKeep = new HashSet<T>(comparer );
HashSet<T> itemsToRemove = new HashSet<T>(comparer );
foreach(T item in items)
{
if (itemsToRemove.Add(item))
{
continue;
}
itemsToKeep.Add(item);
}
itemsToKeep.ExceptWith(itemsToRemove);
如果可能,您可以使用自定义IEqualityComparer<T>
实施来加快广告集的效果。