到目前为止,我有这个:
List<Item> duplicates = items.GroupBy(x => x.Id)
.SelectMany(g => g.Skip(1)).ToList();
List<Item> nonDuplicates = items.GroupBy(x => x.Id)
.Select(x => x.First()).ToList();
是否有更有效的方法(即一次选择)?
示例输入:
Id Value (added for some perspective)
-- -----
1 12
1 909
1231 0
1 577
示例输出:
duplicates -> {1, 909}, {1, 577}
non-duplicates -> {1, 12}, {1231, 0}
答案 0 :(得分:2)
如果确实想要避免多次执行实际分组,从而避免多次迭代源序列,则可以对项目进行分组,将该查询具体化为列表,然后从该列表中获取您想要的信息。
var query = items.GroupBy(x => x.id)
.ToList();
var duplicates = query.SelectMany(group => group.Skip(1));
var nonDuplicates = query.Select(group => group.First());
话虽如此,分组项目并不是特别昂贵的操作,所以这可能实际上并不是特别大的胜利。现有代码“足够好”的可能性相当高。
如果我不确定源序列如果多次迭代会返回相同的项目,或者如果说IQueryable
需要进行往返,那么我最感兴趣的是这样做获取项目的数据库。在这些情况下,这是值得实施的变革。
答案 1 :(得分:2)
获取每个ID的第一个,然后使用Except
获取其他ID。
List<Item> nonDupes = items.GroupBy(x => x.Id).Select(x => x.First()).ToList();
List<Item> dupes = items.Except(nonDupes).ToList();
但是,假设Equals
尚未被覆盖为Id
。
编辑:这是一个小提琴:http://dotnetfiddle.net/4GaPK4
答案 2 :(得分:0)
var result = items.GroupBy(x => x.Id)
.Select(g => new {
Dups = g.Where(g.Count > 1),
NonDups = g.Where(g.Count == 1), })
.ToList();
答案 3 :(得分:0)
不是在一个查询中,但可以通过使用MoreLinq中的DistinctBy
来实现更有效的方法:
var nonDuplicates = items.DistinctBy(i => i.Id);
var duplicates = items.Except(nonDuplicates);