将列表拆分为重复和非重复列表

时间:2014-02-26 21:20:54

标签: c# linq

到目前为止,我有这个:

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}

4 个答案:

答案 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);