随笔7.grdlist重复检测,以及去重

1 阅读5分钟

1.去重检测

使用 LINQ 可以更简洁地实现这个功能。我们可以通过 LINQ 将 DataGridView 的行数据按指定列进行组合并去重。以下是使用 LINQ 实现的示例代码:

 Public Function IsDuplicateExists(gdv As DataGridView, columns As List(Of String)) As Boolean
     ' 列名集合が空の場合、False を返す
     If columns Is Nothing OrElse columns.Count = 0 Then
         Return False
     End If

     ' gdv から全ての行を取得し、新規行は除外する
     Dim rows = gdv.Rows.Cast(Of DataGridViewRow)().Where(Function(row) Not row.IsNewRow)

     ' 指定された列に基づいてキーを作成し、重複をチェックする
     Dim uniqueRows = rows.Select(Function(row) String.Join(",", columns.Select(Function(col) If(row.Cells(col).Value IsNot Nothing, row.Cells(col).Value.ToString(), String.Empty)))).Distinct()

     ' 重複がある場合、uniqueRows の件数が元の行数より少なくなる
     Return uniqueRows.Count() < rows.Count()
 End Function

代码说明:

  1. gdv.Rows.Cast(Of DataGridViewRow)() :使用 Cast 方法将 DataGridViewRowCollection 转换为可枚举的 IEnumerable(Of DataGridViewRow),这样可以使用 LINQ 查询。
  2. Where(Function(row) Not row.IsNewRow) :过滤掉新行(IsNewRowTrue),以避免处理用户新输入的空行。
  3. Select(Function(row) String.Join(",", ...) :使用 LINQ 的 Select 方法来构建每一行的唯一键,按指定的列进行组合。
  4. Distinct() :通过 Distinct 方法去除重复的键。
  5. uniqueRows.Count() < rows.Count() :比较去重前后的行数,如果 Distinct 之后的行数少于原始行数,表示有重复行,返回 True

优点:

  • 使用 LINQ 简洁地实现了按指定列组合和去重的逻辑。
  • Distinct 方法有效地去除重复项,使代码逻辑清晰。
  • 通过 WhereSelect 方法,LINQ 提供了高效的数据操作和过滤机制。

这样使用 LINQ 的方式不仅简化了代码,而且对去重操作较为直观。

 Public Function IsDuplicateExists(gdv As DataGridView, columns As List(Of String)) As Boolean
     ' 列名集合が空の場合、False を返す
     If columns Is Nothing OrElse columns.Count = 0 Then
         Return False
     End If

     ' gdv から全ての行を取得し、新規行は除外する
     Dim rows = gdv.Rows.Cast(Of DataGridViewRow)().Where(Function(row) Not row.IsNewRow)

     ' 指定された列に基づいてキーを作成し、重複をチェックする
     Dim uniqueRows = rows.Select(Function(row) String.Join(",", columns.Select(Function(col) If(row.Cells(col).Value IsNot Nothing, row.Cells(col).Value.ToString(), String.Empty)))).Distinct()

     ' 重複がある場合、uniqueRows の件数が元の行数より少なくなる
     Return uniqueRows.Count() < rows.Count()
 End Function
Private Sub butCheckGrdList_Click(sender As Object, e As EventArgs) Handles butCheckGrdList.Click
    Try
        ' 重複チェックを行う列名リストを作成
        Dim columnsToCheck As New List(Of String) From {"TRI_NAME", "TRI__NAME_RYAKU", "TRI_ADDRESS1", "TRI_TEL"}

        ' 列名リストが空かどうかを確認する
        If columnsToCheck Is Nothing OrElse columnsToCheck.Count = 0 Then
            MessageBox.Show("重複データをチェックする列が指定されていません。", "警告", MessageBoxButtons.OK, MessageBoxIcon.Warning)
            Return
        End If

        ' IsDuplicateExists メソッドを呼び出し、DataGridView と列名リストを渡す
        Dim hasDuplicates As Boolean = IsDuplicateExists(grdList, columnsToCheck)

        ' 重複データがあるかどうかの結果に基づいてメッセージを表示
        If hasDuplicates Then
            MessageBox.Show("重複データが存在します!", "警告", MessageBoxButtons.OK, MessageBoxIcon.Warning)
        Else
            MessageBox.Show("重複データはありません。", "情報", MessageBoxButtons.OK, MessageBoxIcon.Information)
        End If

    Catch ex As Exception

    End Try
End Sub

2.对grdList去重返回DataTable

进一步优化 GetDistinctDataTable 方法

我们将采用以下步骤:

  1. 确保所有列值正确处理,即使它们包含空值。
  2. 使用更明确的唯一性检查,以避免遗漏任何唯一行数据。
  3. 提供更详细的错误信息,以便调试。

以下是优化后的代码:

Public Function GetDistinctDataTable(gdv As DataGridView, columns As List(Of String)) As DataTable
    ' 如果列名集合为空或不存在,返回一个空的 DataTable
    If columns Is Nothing OrElse columns.Count = 0 Then
        Return New DataTable()
    End If

    ' 创建一个新的 DataTable 来存储结果
    Dim resultTable As New DataTable()

    ' 根据传入的列名创建 DataTable 的列,所有列的数据类型都设为 String
    For Each colName In columns
        resultTable.Columns.Add(colName, GetType(String))
    Next

    ' 使用 HashSet 来存储唯一的行数据字符串
    Dim uniqueValues As New HashSet(Of String)()

    ' 从 DataGridView 中获取所有非新行
    Dim rows = gdv.Rows.Cast(Of DataGridViewRow)().Where(Function(row) Not row.IsNewRow)

    ' 遍历每一行,提取指定列的值
    For Each row In rows
        ' 构建唯一的行数据字符串(列值用逗号分隔)
        Dim rowData As String = String.Join(",", columns.Select(Function(col) 
            Dim cellValue = row.Cells(col).Value
            Return If(cellValue IsNot Nothing, cellValue.ToString().Trim(), String.Empty)
        End Function))

        ' 检查 rowData 是否已经存在于 HashSet 中
        If uniqueValues.Add(rowData) Then
            ' 将唯一的行数据添加到 DataTable 中
            resultTable.Rows.Add(rowData.Split(","c))
        End If
    Next

    ' 返回去重后的 DataTable
    Return resultTable
End Function

关键改进点

  1. 修正列值处理

    • 使用 Trim() 方法移除列值的前后空格,确保比较的一致性。
  2. String.Join 拼接

    • 确保列值拼接的字符串格式一致,以便进行准确的去重。
  3. 确保 HashSet 去重逻辑正确

    • HashSet 自动处理重复数据,确保每一条唯一数据只添加一次。

调试和验证

  1. 验证数据

    • 确保 DataGridView 中的数据按预期显示,列名和数据类型都正确。
  2. 检查列名

    • 确保 columns 列表中的列名在 DataGridView 中实际存在。
  3. 调试信息

    • 你可以在代码中添加调试信息来检查每一步是否按预期工作。例如,可以在循环中打印每一行的数据字符串。

调试示例

For Each row In rows
    ' 构建唯一的行数据字符串(列值用逗号分隔)
    Dim rowData As String = String.Join(",", columns.Select(Function(col) 
        Dim cellValue = row.Cells(col).Value
        Return If(cellValue IsNot Nothing, cellValue.ToString().Trim(), String.Empty)
    End Function))
    
    ' 打印调试信息
    Debug.WriteLine($"Processing row: {rowData}")

    ' 检查 rowData 是否已经存在于 HashSet 中
    If uniqueValues.Add(rowData) Then
        ' 将唯一的行数据添加到 DataTable 中
        resultTable.Rows.Add(rowData.Split(","c))
    End If
Next

这段代码将帮助你查看每一行的数据,并检查哪些数据被添加到了结果 DataTable 中。如果问题依旧存在,请提供更详细的数据样本和任何错误信息,以便进一步调试。