在 C# 中使用 Linq 创建一个仅包含唯一值的 DataTable

Posted

技术标签:

【中文标题】在 C# 中使用 Linq 创建一个仅包含唯一值的 DataTable【英文标题】:Create a DataTable containing only unique values with Linq in C# 【发布时间】:2020-11-25 09:42:54 【问题描述】:

我有一个数据表dt_Candidates

      Candidate      |   First Name   |   Last Name   
 --------------------|----------------|--------------- 
  John, Kennedy      | John           | Kennedy       
  Richard, Nixon     | Richard        | Nixon         
  Eleanor, Roosevelt | Eleanor        | Roosevelt     
  Jack, Black        | Jack           | Black         
  Richard, Nixon     | Richard        | Nixon         

我想在没有嵌套循环的情况下创建,最好使用 Linq,这是一个包含 ONLY 唯一值的 DataTable,例如 dt_Candidates2:

      Candidate      |   First Name   |   Last Name   
 --------------------|----------------|--------------- 
  John, Kennedy      | John           | Kennedy       
  Eleanor, Roosevelt | Eleanor        | Roosevelt     
  Jack, Black        | Jack           | Black         

还有一个名为 RejectedCandidates 的列表或数组,其中包含不同的重复项

RejectedCandidates = "Richard, Nixon"

【问题讨论】:

您想根据Candidate 列强制执行唯一性吗? 我不认为 LINQ 是一个很好的工具。为表添加主键并调整创建表的逻辑以使用它(或捕获添加重复值时抛出的错误) @PeterCsala 是的,我愿意!就整个逻辑而言,我只是再次改变主意...另一种解决方法可能是创建 RejectedCandidates 并在面对 ForEach 循环以处理单个值检查 RejectedCandidates.Any(row("Candidate").ToString.Contains) 所以它不会工作 RejectedCandidates它可以提供可行的反馈... @CaiusJard 感谢您提供的有用提示!我对 C# 还是很陌生,而且我曾经发现 Linq 非常方便,我认为我的逻辑被单纯的曝光效果所破坏! :) 我最喜欢的一句话是“LINQ 是一把锤子……但不是每个问题都是钉子”;) 【参考方案1】:

如前所述,我认为这里不需要 LINQ。它可以是这样的:

DataTable dt = new DataTable();
dt.Columns.Add("Candidate");
dt.Columns.Add("First");
dt.Columns.Add("Last");
dt.PrimaryKey = new [] dt.Columns["Candidate"] ; //means that dt.Find() will work

while(...)
  string candidate = ...

  if(dt.Rows.Find(candidate) != null)
    RejectList.Add(...);
  else
    dt.Rows.Add(...);

避免为此在 DataTable 上使用 LINQ 的 .Any。不仅因为它需要强制转换步骤或扩展库 (see here) 来进行操作很痛苦,而且它会使用循环来查找您寻找的信息; PrimaryKey 的内置机制使用哈希表进行更快的查找。

【讨论】:

将 PrimaryKey 添加到具有重复项的表中会抛出 System.ArgumentException: These columns don't currently have unique valuesSystem.Data.ConstraintException: Column 'Candidate' is constrained to be unique. Value 'Richard, Nixon' is already present,具体取决于添加行的时间(在 PK 之前或之后)。我在您的解决方案中缺少什么吗? 您应该将 PK 添加到没有重复项的表中。答案中的代码创建一个新表,添加列,添加一个键,然后填充表。在填充时,它正在检查 if 值是否存在,如果存在,则将值放入拒绝列表中。换一种说法;无论你有什么用重复的代码填充一个表,用这个概念替换它【参考方案2】:
var dt = new DataTable

    Columns = "Candidate", "First Name", "Last Name",
    Rows = 
    
        new object []  "John, Kennedy", "John", "Kennedy",
        new object []  "Richard, Nixon", "Richard", "Nixon",
        new object []  "Eleanor, Roosevelt", "Eleanor", "Roosevelt",
        new object []  "Jack, Black", "Jack", "Black",
        new object []  "Richard, Nixon", "Richard", "Nixon",
    
;

您可以使用分组(groupBy)查找重复项,将其过滤掉,然后创建一个新的DataTable,使用DataTableExtensions.CopyToDataTable扩展方法:

var dt2 = dt.AsEnumerable()

            .GroupBy(r => r["Candidate"])
            .Where(g => g.Count() == 1)

            .Select(g => g.First())
            .CopyToDataTable();

【讨论】:

以上是关于在 C# 中使用 Linq 创建一个仅包含唯一值的 DataTable的主要内容,如果未能解决你的问题,请参考以下文章

使用 Entity Framework 仅计算值的 LINQ 查询

C# Linq 计算具有特定值的 XML 段

C# LINQ 在列表中查找重复项

如何在 LINQ C# 中仅过滤 2 列分组中的最后一个值

如何在 LINQ SQL C# 中返回动态周数?

如何在 Asp.net MVC C# 中使用 Linq 从多个表中选择具有最大计数值的记录