【问题标题】:Get a list of duplicates from list based on properties comparison根据属性比较从列表中获取重复项列表
【发布时间】:2017-12-10 00:27:04
【问题描述】:

我有一个List<Demo>

public class Demo
{
    public Demo()
    {

    }

    public int Id { get; set; }
    public string Name { get; set; }
    public string Title { get; set; }
}

Id 属性对于列表中的每条记录都是唯一的。

如何从原始 List<Demo> 中获得 List<Demo>,其中包含名称和标题相同的所有重复项。

到目前为止我做了什么,但我得到的是一条记录:

List<Demo> demo = new List<Demo>();

demo.Add(new Demo()
{
    Id = 1,
    Name = "Demo",
    Title = "A"
});

demo.Add(new Demo()
{
    Id = 2,
    Name = "Demo",
    Title = "A"
});

demo.Add(new Demo()
{
    Id = 3,
    Name = "Demo",
    Title = "A"
});

demo.Add(new Demo()
{
    Id = 4,
    Name = "Demo1",
    Title = "A"
});

demo.Add(new Demo()
{
    Id = 5,
    Name = "Demo2",
    Title = "A"
});

然后我在做:

var duplicates = demo.GroupBy(t => new { t.Name, t.Title })
                     .Where(t => t.Count() > 1)
                     .Select(g => g.Key).ToList();

从上面的示例中,我应该得到一个 List&lt;Demo&gt;,其中前 3 个项目的 ID 为:1、2、3,因为名称和标题相同。

【问题讨论】:

标签: c# linq


【解决方案1】:

听起来您所缺少的只是一个SelectMany 电话。目前,您正在创建所有适当的组并过滤到具有多个条目的组 - 但如果您想要一个单一的平面列表,您需要将这些组展平为它们的元素:

var duplicates = demo
    .GroupBy(t => new { t.Name, t.Title })
    .Where(t => t.Count() > 1)
    .SelectMany(x => x) // Flatten groups to a single sequence
    .ToList();

请注意,这并不意味着结果列表中的每个条目都将具有相同的名称和标题。这确实意味着每个条目都将具有与至少一个其他条目相同的名称/标题组合。

【讨论】:

  • 你还能建议如何忽略空字符串吗?例如,如果我有名称和标题为空字符串的记录
  • @user2818430:您应该在自己尝试之后将其作为一个单独的问题提出。 (提示:在开头使用 Where 子句过滤它们。)
【解决方案2】:

你快到了。

Groupby 将序列分成组。每个组都有一些共同点:该组的Key。每个组都是一个序列,包含原始序列中与键匹配的所有元素。

您的密钥是new { t.Name, t.Title }。您的原始序列将分为具有相同名称/标题的对象组:

  • Group Demo / A包含三个元素:Id = 1、Id = 2、Id = 3
  • Group Demo1 / A 仅包含元素 Id = 4
  • Group Demo2 / A 仅包含元素 Id = 5

分组后的Where 返回IGrouping 的序列。该序列仅包含一个元素:唯一具有多个元素的组。这是具有关键 Demo/A 的组。

您的规范不是 IGrouping 序列(其中每个组是 Demo 的序列),而是一个 Demo 列表,其中包含具有重复名称/标题的所有元素。

这意味着您必须从 Where 之后的所有组中获取元素(在您的示例中,此序列仅包含一个组)并将所有这些组连接到一个序列中。这是由Enumerable.SelectMany完成的

IEnumerable<Demo> duplicates = demo
    .GroupBy(demoElement => new {demoElement.Name, demoElement.Title})
    .Where(group => group.Skip(1).Any())
    .SelectMany(group => group);

SelectMany 获取每一组的序列并将所有序列连接成一个序列。

对了,你有没有注意到我没有使用 Count() 来检测是否有重复,而是Skip(1).Any()。如果您的一个组有数百个元素,那么在第一个元素之后停止计数就足够了。计算所有元素来检测是否超过一个是浪费计算能力。

最后一个提示:如果您不确定是否需要它,请不要使用ToList()。如果你的代码的用户只想要第一个元素,或者前几个元素,如果你把所有元素都计算成一个列表,那就太浪费了。尽可能长时间地保留IEnumerable&lt;Demo&gt;

【讨论】:

    【解决方案3】:

    如果您有超过 1 个头衔并且名称相同,那么您想要的就是她的。假设最后一个对象是Demo1A

    var duplicates = demo.GroupBy(t => new { t.Name, t.Title }).Where(t => t.Count() > 1)
                         .Select(x => new { Count = x.Count(), Values = x.Select(y => y)})
                         .ToList();
    

    这是做什么的,为您提供计数和所有分组值(基于名称和标题)及其计数。

    【讨论】:

      【解决方案4】:

      下面的代码可以帮助你,

      列出重复项 = demo.GroupBy(grp => new { grp.Name, grp.Title }).SelectMany(selm => selm.Skip(1)).Distinct().ToList();

      按照您的预期答案工作正常。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-03-05
        • 2023-03-16
        • 2017-05-06
        • 1970-01-01
        • 2021-11-28
        • 2011-04-25
        相关资源
        最近更新 更多