在一列中查找重复值,并从另一列中检查以删除最近的行。

huangapple go评论68阅读模式
英文:

Find duplicate value in a column, and check from another one to delete the recent rows

问题

I'm working on an Excel VBA table where I can find some duplicate values in column 'I'. I need to delete the last duplicate rows (those without manual data added). For this, I created a column in "CF" with the date when the rows were created. So, I need to delete duplicate data in column 'I' where the date is the most recent.

Here's my VBA macro:

Sub supprimerDoublons()
    ' Code goes here...
End Sub

It runs through the rows, stores values in a dictionary, and checks the date in column 'CF' to decide which rows to delete. However, sometimes it doesn't work correctly and deletes the wrong row. I've tried various things but can't replicate the bug consistently. If anyone has had a similar issue or has alternative solutions, please help.

英文:

I'm working on a excel vba table where I can find some duplicate value in columns 'I'.
I need to delete the last duplicate rows (This one who didn't had manual data added).
For doing that I created a column in "CF" with the date where the rows was created.
So I need to delete duplicate data in column 'I' where the date is the most recent.

Exemple :
Row 1 column 'I' : 9095-IDE
Row 1 column 'CF' : 21/06/23 14:50:16

Row 2 column 'I' : 9095-IDE
Row 2 column 'CF' : 26/06/23 14:45:42

Then Row 2 would be delete.
That work, but sometime I don't know why, it will be row 1 who will be delete.
That shit cause in a lot column between I & CF, user need to write manual data in.. So if I delete the wrong one, they lost their data...

Here my VBA macro :

Sub supprimerDoublons()
    Dim derniereligne As Long
    Dim dict As Object
    Dim a As Long
    Dim x As Variant
    Dim rngToDelete As Range
    
    Set dict = CreateObject("Scripting.Dictionary")
    
    derniereligne = ActiveSheet.Cells(Rows.Count, "I").End(xlUp).Row
    
    For a = 5 To derniereligne
        x = Cells(a, "I").Value
        If Not dict.exists(x) Then
            dict.Add x, a
        Else
            If Cells(a, "CF").Value > Cells(dict(x), "CF").Value Then
                If rngToDelete Is Nothing Then
                    Set rngToDelete = Rows(a)
                Else
                    Set rngToDelete = Union(rngToDelete, Rows(a))
                End If
            Else
                If rngToDelete Is Nothing Then
                    Set rngToDelete = Rows(dict(x))
                Else
                    Set rngToDelete = Union(rngToDelete, Rows(dict(x)))
                End If
                dict.Item(x) = a
            End If
        End If
    Next a
    
    If Not rngToDelete Is Nothing Then
        rngToDelete.Delete
    End If
    
End Sub

It will run on rows, stock a value in dictionnary. If a same value is already in dictionnary, it will check in CF the date and stock in rngToDelete the right one to delete.

In paper, it look amazing, but can't find why sometime it didn't work well, and delete the wrong row ...

Can someone help me ?

I tried many thing, but I can't replicate the bug everytime, it like random ..
Would like to know if anyone had similare trouble, or have another solutions ..

答案1

得分: 2

删除“重复”行:保留列中第一个(最小)值

快速修复

  • 您的If语句是错误的,因为它没有涵盖列CF中的单元格相等的情况,这会落入您的代码中的Else语句中。要更正此问题,请使用以下内容:

    If Cells(a, "CF").Value >= Cells(dict(x), "CF").Value Then
    

在一列中查找重复值,并从另一列中检查以删除最近的行。

改进

  • 使用更多变量和一个函数来合并单元格,从而大大提高代码的可读性。
  • 通过正确的工作表限定所有范围、单元格、行和列,以便代码可以正常工作,如果您(正确地)使用例如Set ws = ThisWorkbook.Worksheets("Sheet1")指定工作表,而不依赖于活动工作表。
Sub DeleteDuplicateRows()

    Dim ws As Worksheet: Set ws = ActiveSheet ' 改进!
    Dim LastRow As Long: LastRow = ws.Cells(ws.Rows.Count, "I").End(xlUp).Row

    Dim dict As Object: Set dict = CreateObject("Scripting.Dictionary")
    dict.CompareMode = vbTextCompare ' 即 'A = a'

    Dim rngToDelete As Range, NewValue, PreviousValue, sStr As String
    Dim r As Long, pr As Long

    For r = 5 To LastRow
        sStr = CStr(ws.Cells(r, "I").Value)
        If Not dict.Exists(sStr) Then
            dict(sStr) = r
        Else
            pr = dict(sStr)
            PreviousValue = ws.Cells(pr, "CF").Value
            NewValue = ws.Cells(r, "CF").Value
            If NewValue >= PreviousValue Then
                Set rngToDelete = RefCombinedRange(rngToDelete, ws.Rows(r))
            Else
                dict(sStr) = r
                Set rngToDelete = RefCombinedRange(rngToDelete, ws.Rows(pr))
            End If
        End If
    Next r

    If Not rngToDelete Is Nothing Then
        ' 测试时使用:
        rngToDelete.Select
        ' 测试完成后,改用以下内容。
        'rngToDelete.Delete xlShiftUp
    End If

End Sub

Function RefCombinedRange(ByVal urg As Range, ByVal arg As Range) As Range
    If urg Is Nothing Then Set urg = arg Else Set urg = Union(urg, arg)
    Set RefCombinedRange = urg
End Function
英文:

Delete 'Duplicate' Rows: Keep the First (Top-Most) Smallest Value in a Column

A Quick Fix

  • Your If statement is wrong because it doesn't cover the case when the cells in column CF are equal which is falling under the Else statement in your code. To correct this, use:

    If Cells(a, "CF").Value >= Cells(dict(x), "CF").Value Then
    

在一列中查找重复值,并从另一列中检查以删除最近的行。

An Improvement

  • Use more variables and a function to combine the cells to vastly improve the code's readability.
  • Qualify all ranges, cells, rows, and columns with the correct worksheet so the code would work if you would (correctly) specify the worksheet with e.g. Set ws = ThisWorkbook.Worksheets("Sheet1"), not relying on the active sheet.

<!-- language: lang-vb -->

Sub DeleteDuplicateRows()
    
    Dim ws As Worksheet: Set ws = ActiveSheet &#39; improve!
    Dim LastRow As Long: LastRow = ws.Cells(ws.Rows.Count, &quot;I&quot;).End(xlUp).Row
    
    Dim dict As Object: Set dict = CreateObject(&quot;Scripting.Dictionary&quot;)
    dict.CompareMode = vbTextCompare &#39; i.e. &#39;A = a&#39;
    
    Dim rngToDelete As Range, NewValue, PreviousValue, sStr As String
    Dim r As Long, pr As Long
    
    For r = 5 To LastRow
        sStr = CStr(ws.Cells(r, &quot;I&quot;).Value)
        If Not dict.Exists(sStr) Then
            dict(sStr) = r
        Else
            pr = dict(sStr)
            PreviousValue = ws.Cells(pr, &quot;CF&quot;).Value
            NewValue = ws.Cells(r, &quot;CF&quot;).Value
            If NewValue &gt;= PreviousValue Then
                Set rngToDelete = RefCombinedRange(rngToDelete, ws.Rows(r))
            Else
                dict(sStr) = r
                Set rngToDelete = RefCombinedRange(rngToDelete, ws.Rows(pr))
            End If
        End If
    Next r
    
    If Not rngToDelete Is Nothing Then
        &#39; Test with:
        rngToDelete.Select
        &#39; When done testing, use the following instead.
        &#39;rngToDelete.Delete xlShiftUp
    End If
    
End Sub

Function RefCombinedRange(ByVal urg As Range,ByVal arg As Range) As Range
    If urg Is Nothing Then Set urg = arg Else Set urg = Union(urg, arg)
    Set RefCombinedRange = urg
End Function

答案2

得分: 0

以下是代码的翻译部分:

Sub supprimerDoublons()
    Dim ws As Worksheet: Set ws = ActiveSheet ' 改进!
    ' 过滤器
    Dim filterRange As Range
    Dim filterCriteria As Variant

    If ws.AutoFilterMode Then
    ' 记住现有的过滤器范围和条件
    Set filterRange = ws.AutoFilter.Range
    filterCriteria = ws.AutoFilter.Filters
    
    ws.ShowAllData ' 清除过滤器

    Dim LastRow As Long: LastRow = ws.Cells(ws.Rows.Count, "I").End(xlUp).Row
    
    Dim dict As Object: Set dict = CreateObject("Scripting.Dictionary")
    dict.CompareMode = vbTextCompare ' 即 'A = a'
    
    Dim rngToDelete As Range, NewValue, PreviousValue, sStr As String
    Dim r As Long, pr As Long
    
    For r = 5 To LastRow
        sStr = CStr(ws.Cells(r, "I").Value)
        If Not dict.Exists(sStr) Then
            dict(sStr) = r
        Else
            pr = dict(sStr)
            PreviousValue = ws.Cells(pr, "CF").Value
            NewValue = ws.Cells(r, "CF").Value
            If NewValue >= PreviousValue Then
                Set rngToDelete = RefCombinedRange(rngToDelete, ws.Rows(r))
            Else
                dict(sStr) = r
                Set rngToDelete = RefCombinedRange(rngToDelete, ws.Rows(pr))
            End If
        End If
    Next r
    
    If Not rngToDelete Is Nothing Then
        ' 测试用:
        'rngToDelete.Select
        '测试完成后,改用以下代码
        rngToDelete.Delete xlShiftUp
    End If
    
    ' 恢复先前的过滤器
    filterRange.AutoFilter Field:=5, Criteria1:=filterCriteria(5)
    ' 对于每个具有过滤器的列,请根据需要调整列和条件的索引
    filterRange.AutoFilter Field:=7, Criteria1:=filterCriteria(7)
    filterRange.AutoFilter Field:=8, Criteria1:=filterCriteria(8)
End If
    
End Sub

Function RefCombinedRange(ByVal urg As Range, ByVal arg As Range) As Range
    If urg Is Nothing Then Set urg = arg Else Set urg = Union(urg, arg)
    Set RefCombinedRange = urg
End Function

希望这有助于解决你在执行代码时遇到的错误。如果你需要进一步的帮助,请提供更多关于错误的信息。

英文:

I do something like this :

Sub supprimerDoublons()
    Dim ws As Worksheet: Set ws = ActiveSheet &#39; improve!
    &#39;Les filtres
    Dim filterRange As Range
    Dim filterCriteria As Variant

    If ws.AutoFilterMode Then
    &#39; M&#233;moriser la plage et les crit&#232;res de filtre existants
    Set filterRange = ws.AutoFilter.Range
    filterCriteria = ws.AutoFilter.Filters
    
    ws.ShowAllData &#39; Effacer les filtres

    Dim LastRow As Long: LastRow = ws.Cells(ws.Rows.Count, &quot;I&quot;).End(xlUp).Row
    
    Dim dict As Object: Set dict = CreateObject(&quot;Scripting.Dictionary&quot;)
    dict.CompareMode = vbTextCompare &#39; i.e. &#39;A = a&#39;
    
    Dim rngToDelete As Range, NewValue, PreviousValue, sStr As String
    Dim r As Long, pr As Long
    
    For r = 5 To LastRow
        sStr = CStr(ws.Cells(r, &quot;I&quot;).Value)
        If Not dict.Exists(sStr) Then
            dict(sStr) = r
        Else
            pr = dict(sStr)
            PreviousValue = ws.Cells(pr, &quot;CF&quot;).Value
            NewValue = ws.Cells(r, &quot;CF&quot;).Value
            If NewValue &gt;= PreviousValue Then
                Set rngToDelete = RefCombinedRange(rngToDelete, ws.Rows(r))
            Else
                dict(sStr) = r
                Set rngToDelete = RefCombinedRange(rngToDelete, ws.Rows(pr))
            End If
        End If
    Next r
    
    If Not rngToDelete Is Nothing Then
        &#39; Test with:
        &#39;rngToDelete.Select
        &#39;When done testing, use the following instead.
        rngToDelete.Delete xlShiftUp
    End If
    
        &#39; R&#233;tablir les filtres pr&#233;c&#233;dents
    filterRange.AutoFilter Field:=5, Criteria1:=filterCriteria(5)
    &#39; R&#233;p&#233;tez cette ligne pour chaque colonne avec des filtres, en ajustant l&#39;index de la colonne et du crit&#232;re si n&#233;cessaire
    filterRange.AutoFilter Field:=7, Criteria1:=filterCriteria(7)
    filterRange.AutoFilter Field:=8, Criteria1:=filterCriteria(8)
End If
    
End Sub

Function RefCombinedRange(ByVal urg As Range, ByVal arg As Range) As Range
    If urg Is Nothing Then Set urg = arg Else Set urg = Union(urg, arg)
    Set RefCombinedRange = urg
End Function

But I got an error 450 on execution, can't find why ..

huangapple
  • 本文由 发表于 2023年6月26日 22:09:12
  • 转载请务必保留本文链接:https://go.coder-hub.com/76557474.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定