【问题标题】:Loop on datatable after deleting columns in C#在 C# 中删除列后循环数据表
【发布时间】:2021-06-22 12:03:01
【问题描述】:

我正在尝试从具有空单元格或空单元格的数据表中删除行,同时我检查列是否包含超过百分比的空单元格,如果是这种情况,我会删除整个列。我尝试这样进行:

 private DataTable CleanData()
        {
            var dt = BindData(openFileDialog1.FileName);
            for (var j = dt.Columns.Count-1; j >= 0; j--)
            {
                short count = 0;
                for (var i = dt.Rows.Count - 1; i >= 0; i--)
                {
                    if (!string.IsNullOrEmpty(dt.Rows[i][j].ToString())) continue;
                    count++;
                }

                var percentage = count * 100.0 / dt.Rows.Count;
                if (percentage > 10)
                {
                    dt.Columns.RemoveAt(j);
                    textFile.Text += " " + j + " ";
                }
            }

            dt.AcceptChanges();

            for (var j = dt.Columns.Count - 1; j >= 0; j--)
            for (var i = dt.Rows.Count - 1; i >= 0; i--)
            {
                if (!string.IsNullOrEmpty(dt.Rows[i][j].ToString())) continue;
                dt.Rows[i].Delete();
            }

            dt.AcceptChanges();
            return dt;
        }

我第一次循环遍历数据表单元格,然后检查一列中空单元格的百分比,如果超过 10%,我删除该列,然后我第二次循环,这次删除每一行有一个空的单元格,但在第二个循环中,当它到达已删除的列索引时,我收到一条错误消息 (System.Data.DeletedRowInaccessibleException),即使它应该在这些列不存在的数据表上循环。 任何线索我搞砸了? 编辑:我进行了建议的更改,但仍然遇到相同的错误

【问题讨论】:

  • 您应该在循环内调用 AcceptChanges 或使用 Remove 而不是 Delete。
  • 我无法用只有 3 列和 3 行的简单表格重现您的问题。但是,在行上的循环中存在错误。退出条件应该是 i >= 0 而不是 i > 0
  • 从数组中删除时,总是从末尾开始并移到开头:for (var j = dt.Columns.Count - 1; j >= 0 ; j--)
  • 对,但这无法解释与已删除行相关的异常。
  • @Crowcoder 我使用 delete 的部分工作正常(因为我从那里开始,然后我添加了检查列上 % 的循环。

标签: c# .net winforms


【解决方案1】:

我认为您遇到的是循环检查 % 和删除列的意外副作用。您从 0 索引列(第一列)开始。检查然后如果为空则删除。反过来做......从最后一列开始,然后回到 0,这就是原因。

假设您从 3 列的表开始,因此您的循环计数器旨在为 0、1、2。第一次循环,循环计数器 0。您确定数据良好,没有删除。计数器 = 1(第 2 列)。确定由于 % empty 需要将其删除。现在你删除列[1]。这将移动 WAS 列 [2],现在变为列 [1],您的计数器现在前进到 2。您从未检查过第三列是什么。

如果你反过来做,你从第[3]列开始,检查它,发现它没问题(或者不是,不在乎)。现在向下 1 到 column[2] 并确定删除。所以它被删除了,column[3] 现在是 column[2]。现在您检查 column[0] 并完成没有问题。

在检查 ROWS 时您已经在执行此操作(从末尾开始并返回)。同样的原则也适用。

至于您删除 ROW 的循环,我会反转您的循环。

Outer loop per ROW (last row first, working back)
{
   Inner loop per COLUMN
   {
      if any single column qualifies to delete the row
      {
         dt.rows[i].Delete();
         break;  [break out of the column checking loop]
      }
   }
   [ continue with each ROW]
}

由于您现有的外部循环是每列,如果您处理第 1 列并删除第 5 行,然后到达第 2 列并尝试再次删除第 5 行,那是您的失败。

通过首先检查单行的所有列,并在有资格删除时立即退出,您就完成了该行,无需考虑查看任何其他列。移至下一行进行处理。

【讨论】:

  • 你这么说是有道理的
  • @Isumairu,修改后的答案,请查看
  • 非常感谢,实际上先循环行更实用,当我像您一样使用if 条件时,它与我的代码一起工作。
猜你喜欢
  • 2011-03-10
  • 2017-10-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-12-03
  • 1970-01-01
  • 2023-03-18
  • 2015-12-06
相关资源
最近更新 更多