C# - 从DataTable中删除具有相同列值的行

3

我有一个DataTable,看起来像这样:

 ID   Name    DateBirth
.......................
 1     aa      1.1.11
 2     bb      2.3.11
 2     cc      1.2.12
 3     cd      2.3.12

哪种方法最快地删除具有相同ID的行,以获得如下结果(保留第一次出现,删除其余行):
 ID   Name    DateBirth
.......................
 1     aa      1.1.11
 2     bb      2.3.11
 3     cd      2.3.12

我不想对表格行进行两次处理,因为行数很大。 如果可能的话,我想使用一些LinQ,但我想这可能会是一个巨大的查询,我必须使用比较器。


你尝试过什么?只是关于ID吗?其他字段都不相关吗? - DerApe
常规做法是使用两个for循环,验证每一行的ID字段。如果它是重复的,就将其删除。但这种方法基础且效率低下。是的,其他字段都是不相关的,只有ID是重要的。 - darkdante
6个回答

10
你可以使用 LINQ to DataTable,基于列 ID 进行去重。你可以在这个列上进行 分组,然后选择第一个:select first。
  var result = dt.AsEnumerable()
                 .GroupBy(r => r.Field<int>("ID"))
                 .Select(g => g.First())
                 .CopyToDataTable();

3

我在解决同样的问题时发现它非常有趣,想分享我的发现。

  1. 如果行应该基于所有列都是唯一的。
DataTable newDatatable = dt.DefaultView.ToTable(true, "ID", "Name", "DateBirth");

您在此提到的列,只有这些列将在newDatatable中返回。

  1. 如果基于一列进行去重并且该列的类型为 int ,那么我会选择使用LINQ查询。
  DataTable newDatatable = dt.AsEnumerable()
                           .GroupBy(dr => dr.Field<int>("ID"))
                           .Select(dg => dg).Take(1)
                           .CopyToDataTable();

如果基于一个字符串类型的列来进行去重,我会倾向于使用循环。
List<string> toExclude = new List<string>();
for (int i = 0; i < dt.Rows.Count; i++)
{
    var idValue = (string)dt.Rows[i]["ID"];
    if (toExclude.Contains(idValue))
    {
        dt.Rows.Remove(dt.Rows[i]);
        i--;
    }
    toExclude.Add(glAccount);
}

第三个是我最喜欢的。

我可能回答了一些问题中没有提到的事情。这是出于善意和一点兴奋而做的。

希望它能有所帮助。


如果“distinct”基于两列而不是一列呢?即在上述情况下为“id”。 - Jogi

2
你可以尝试这个。
DataTable uniqueCols = dt.DefaultView.ToTable(true, "ID");

2

不一定是最高效的方法,但可能是最易读的方法:

table = table.AsEnumerable()
    .GroupBy(row => row.Field<int>("ID"))
    .Select(rowGroup => rowGroup.First())
    .CopyToDataTable();

Linq更加强大。例如,如果您想改变逻辑,并且不选择每个id组的第一行(任意),而是根据DateBirth选择最后一行:

table = table.AsEnumerable()
    .GroupBy(row => row.Field<int>("ID"))
    .Select(rowGroup => rowGroup
                          .OrderByDescending(r => r.Field<DateTime>("DateBirth"))
                          .First())
    .CopyToDataTable();

2
  1. 为每个ID获取记录数量
var rowsToDelete = 
    (from row in dataTable.AsEnumerable()
    group row by row.ID into g
    where g.Count() > 1
  1. 确定要保留哪个记录(不知道您的标准;我会按出生日期排序,然后按 Name 排序并选择第一条记录),然后选择其余的记录。
select g.OrderBy( dr => dr.Field<DateTime>( "DateBirth" ) ).ThenBy( dr => dr.Field<string>( "Name" ) ).Skip(1))
  1. 压平
.SelectMany( g => g );
  1. 删除行
rowsToDelete.ForEach( dr => dr.Delete() );
  1. 接受更改
dataTable.AcceptChanges();

1
这是一种实现方法, 你只需要使用moreLinq库,并使用它的函数DistinctBy

代码:

protected void Page_Load(object sender, EventArgs e)
{
  var DistinctByIdColumn = getDT2().AsEnumerable()
                                   .DistinctBy(
                                   row => new { Id = row["Id"] });
  DataTable dtDistinctByIdColumn = DistinctByIdColumn.CopyToDataTable();
}


public DataTable getDT2()
{
   DataTable dt = new DataTable();
   dt.Columns.Add("Id", typeof(string));
   dt.Columns.Add("Name", typeof(string));
   dt.Columns.Add("Dob", typeof(string));
   dt.Rows.Add("1", "aa","1.1.11");
   dt.Rows.Add("2", "bb","2.3.11");
   dt.Rows.Add("2", "cc","1.2.12");
   dt.Rows.Add("3", "cd","2.3.12");
   return dt;
}

输出:正如你所期望的。

enter image description here

查看更多Linq示例代码,请访问我的博客


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接