简体   繁体   English

Excel 2016 VBA删除具有特定条件的行

[英]Excel 2016 VBA Delete Rows with certain criteria

I have the following data. 我有以下数据。 样本数据

If the same task appears more than 2 rows, I need to delete the 3rd, 4th,...duplicate rows. 如果同一任务出现多于2行,则需要删除第3、4,...重复行。 The rows are already sorted by Task name and date modified. 这些行已经按任务名称和修改日期排序。 I only need the most current and 1 prior data (first 2 rows of the same task). 我只需要最新的数据和1个先前的数据(同一任务的前2行)。

In the above image, Row 7, 8, 9, 13, 14, 15, 16, 17, ... should be deleted 在上图中,应删除第7、8、9、13、14、15、16、17 ...行

This isn't as easy to pull of as it first seems it might be. 这并不像初看起来那样容易。 Every time we loop rows and delete the row based on some criteria we run into issues. 每次我们循环行并根据某些条件删除行时都会遇到问题。 We delete the row we are on, and then move to the next row, but because we deleted the row we are on, we are already on the next row. 我们删除我们所在的行,然后移至下一行,但是由于我们删除了我们所在的行,因此我们已经在下一行。 It's like pulling the rug out from underneath us while we are walking. 这就像在走路时从我们下面拉出地毯。

To get around these we usually step backwards through a range and delete, but... because in this case we are only deleting based on what we've already found, we have to go forwards. 为了解决这些问题,我们通常向后退一步并删除,但是...因为在这种情况下,我们仅基于已经找到的内容进行删除,因此我们必须前进。

There's a few ways to deal with this. 有几种方法可以解决这个问题。 I think the safest and most robust way is by looping once to get a count of each term, then looping again and deleting the appropriate amount of rows based on what we found in the first loop. 我认为最安全,最可靠的方法是循环一次以获取每个术语的计数,然后再次循环并根据我们在第一个循环中发现的内容删除相应数量的行。 This way we have LOTS of control over what gets deleted and how far we step in each iteration. 这样,我们可以控制要删除的内容以及每次迭代的步幅。 It also reduces the complexities that we would face in tracking what we've found, how much of it, if we need to delete the current row, and how to deal with the loop if we do. 它还降低了我们在跟踪找到的内容,要删除多少内容(如果需要删除当前行)以及如何处理循环时将面临的复杂性。

Sub removeRows()
    Dim rngRow As Range
    Dim dictTerms As Scripting.Dictionary

    'Initialize Dictionary
    Set dictTerms = New Scripting.Dictionary

    'Load up dictionary using the term as the key and the count as the value
    ' by looping through each row that holds the terms
    For Each rngRow In Sheet1.Rows("3:17")
        'Only upsert to dictionary if we have a valule
        If rngRow.Cells(1, 1).Value <> "" Then
            If dictTerms.Exists(rngRow.Cells(1, 1).Value) Then
                'increment value
                dictTerms(rngRow.Cells(1, 1).Value) = dictTerms(rngRow.Cells(1, 1).Value) + 1
            Else
                'Add to dictionary with value 1
                dictTerms.Add Key:=rngRow.Cells(1, 1).Value, Item:=1
            End If
        End If
    Next rngRow

    Dim intRow As Integer: intRow = 3
    Dim intCounter As Integer
    Dim termCount As Integer

    'Loop through rows starting at intRow(set to 3 above) and ending at 17 (possible max)
    Do While intCounter <= 17

        'Skip blank rows
        If Sheet1.Cells(intRow, 1).Value <> "" Then

            'grab the number of terms encounterd so we know how much to delete
            termCount = dictTerms(Sheet1.Cells(intRow, 1).Value)

            'If it's more than two, then delete the remaining
            If termCount > 2 Then Sheet1.Rows(intRow + 2 & ":" & intRow + termCount - 1).Delete

            'Increment past what we deleted (or found if we didn't delete anything)
            intRow = intRow + 2 + (termCount <> 1)

            'Set the loop counter.
            intCounter = intCounter + termCount

        Else
            'We found a blank, just increment our loop counter
            intCounter = intCounter + 1
            intRow = intRow + 1
        End If
    Loop

End Sub

You'll need to go to Tools>>References and add Microsoft Scripting Runtime (check the checkbox next to it in the big list and hit OK) so we can use the Scripting.Dictionary. 您需要转到工具>>参考,并添加Microsoft脚本运行时(在大列表中选中它旁边的复选框,然后单击确定),以便我们可以使用Scripting.Dictionary。

You'll also need to edit references to Sheet1 to whatever Sheet you are on. 您还需要将对Sheet1的引用编辑到所使用的任何Sheet。 And possibly references to Cells(1,1) changing that first 1 to whatever column we are analyzing (assumed Column "A" here). 并且可能引用了Cells(1,1) ,将第一个1更改为我们正在分析的任何列(此处假定列为“ A”)。 Lastly you'll need to tweak startRow and endRow setting those values to whatever is appropriate for you workbook. 最后,您需要调整startRowendRow将这些值设置为适合您的工作簿的值。

@JNevill, when you mentioned counter for each task, I just thought of something simple, -- now I just need to figure out how to delete the rows with values greater than 2 in the new column...without getting the error Script out of Range or Delete Method of Range Class Failed @JNevill,当您提到每个任务的计数器时,我只是想到了一些简单的方法,现在我只需要弄清楚如何删除新列中值大于2的行...而不会出现错误脚本范围范围类的删除方法失败

'Declare Variables
    Dim tableName As String
    Dim activeSheetName As String
    Dim totalrows As Long


'Identify Active Sheet Name
    activeSheetName = ActiveSheet.Name

'Identify Active Table Name
    tableName = ActiveSheet.ListObjects(1).Name

'Identify total number of rows
    totalrows = Worksheets(activeSheetName).Range(tableName).Rows.count '99

'Insert counter column
Columns("B:B").Select
    Selection.Insert Shift:=xlToRight, CopyOrigin:=xlFormatFromLeftOrAbove

Dim taskCounter As Long
Dim rowNum As Integer
rowNum = 2
taskCounter = 0

For a = 1 To totalrows + 1

    If Range("A" & rowNum) = "" Then
        taskCounter = 0
        Range("B" & rowNum).Value = taskCounter
    Else
        taskCounter = taskCounter + 1
        Range("B" & rowNum).Value = taskCounter
    End If

    rowNum = rowNum + 1
Next a

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM