I have a lot of Excels from different modules with different column layouts (purchase orders, Sales orders, Production orders, etc.).
I want to delete every row that contains value "Invoiced".
I was able to create simple code where only one column ("J") is checked, but I need whole worksheet to be checked.
Private Sub BoomShakalaka_Click()
Application.ScreenUpdating = False
ow = Cells(Rows.Count, "J").End(xlUp).Row
For r = ow To 1 Step -1
If Cells(r, "J") = "Invoiced" Then Rows(r).Delete
Next
Application.ScreenUpdating = True
End Sub
I expect that after I run this function, it will check the whole workbook and delete every row which contains the value "Invoiced".
I want to add here my idea of using arrays instead, so you only access the worksheet when you read the data, and then when you delete the rows.
Option Explicit
Sub deleteInvoiced()
With Application
.ScreenUpdating = False
.DisplayAlerts = False
End With
Dim wb As Workbook: Set wb = ActiveWorkbook 'or ThisWorkbook, or the name of the workbook where data is
Dim ws As Worksheet
Dim R As Long, C As Long, X As Long
Dim lRow As Long, lCol As Long
Dim arrData
For Each ws In wb.Worksheets
lRow = ws.Cells(ws.Rows.Count, 1).End(xlUp).row 'Get the last row in the current sheet
lCol = ws.Cells(1, ws.Columns.Count).End(xlToLeft).Column 'Get the last column in the current sheet
arrData = ws.Range(ws.Cells(1, 1), ws.Cells(lRow, lCol))
For R = UBound(arrData) To LBound(arrData) Step -1
For C = UBound(arrData, 2) To LBound(arrData, 2) Step -1
If arrData(R, C) = "Invoiced" Or arrData(R, C) = "Delivered" Then
'Now delete the rows
ws.Cells(R, C).EntireRow.Delete
Exit For 'Exit here in case multiple "Invoice" or "Delivered" in the same row (WHY?!!). Thanks #Brian.
End If
Next C
Next R
Next ws
With Application
.ScreenUpdating = True
.DisplayAlerts = True
End With
End Sub
PS: There is no error handling, but i leave that to you.
Loop through every cell within every row within activesheet.usedrange:
Private Sub BoomShakalaka_Click()
For r = ActiveSheet.UsedRange.Rows.Count To 1 Step -1
For Each c In ActiveSheet.UsedRange.Rows(r).Cells
If c.Value = "Invoiced" Then
c.EntireRow.Delete
Exit For
End If
Next c
Next r
End Sub
Alternatively, you could do it by using find. This will be faster than my other answer if there is a lot of data:
sub BoomShakalaka_Click()
screenupdating = false
On Error GoTo exitSub
ActiveSheet.UsedRange.SpecialCells(xlLastCell).select
do while true
Cells.Find(What:="Invoiced", After:=ActiveCell, LookIn:=xlFormulas, LookAt:= _
xlPart, SearchOrder:=xlByRows, SearchDirection:=xlPrevious, MatchCase:=False _
, SearchFormat:=False).Activate
Selection.EntireRow.Delete
Loop
exitSub:
screenupdating = True
Exit Sub
end sub
Here's another interesting way. It assumes your data starts in cell A1 and is contiguous.
Option Explicit
Public Sub TestDeleteInvoiced()
Dim wb As Workbook
Dim ws As Worksheet
Dim arr As Variant
Dim arr1() As Variant
Dim row As Long
Dim col As Long
Dim i As Long
Set wb = ActiveWorkbook
i = 1
ReDim arr1(1 To 14)
For Each ws In wb.Worksheets
arr = ws.Range("A1").CurrentRegion
For row = UBound(arr, 1) To LBound(arr, 1) Step -1
For col = UBound(arr, 2) To LBound(arr, 2) Step -1
If arr(row, col) = "Invoiced" Then
arr1(i) = row & ":" & row
i = i + 1
'This If statement ensures that the Join function is less than 255 characters.
If i = 15 Then
ws.Range(Join(arr1, ", ")).EntireRow.Delete
ReDim arr1(1 To 14)
i = 1
End If
Exit For
End If
Next col
Next row
ReDim Preserve arr1(1 To i - 1)
ws.Range(Join(arr1, ", ")).EntireRow.Delete
Next ws
End Sub
Note: Deleting a range of non-contiguous rows cannot exceed a 255 character parameter. Link
Related
Thus anyone have idea on how to resolved this,
I have many Data to Sort, I want to Sort only Column 2 but separated by Space and also I want to delete duplicate values,
Is there any formula to fix this?
Please see Picture Below
I found this Answer on Google, And it Helps me to fix the Problem
Sub SortAndRemoveDuplicates()
Dim Rng As Range
Dim RngArea As Range
Dim sortRng As Range
Dim lr As Long
Dim i As Long
Dim startRow As Long
If Selection.Columns.Count > 1 Or Selection.Column <> 2 Then
MsgBox "Please select data in column B only and then try again...", vbExclamation
Exit Sub
End If
On Error Resume Next
Set Rng = Selection.SpecialCells(xlCellTypeConstants, 1)
On Error GoTo 0
If Rng Is Nothing Then Exit Sub
Application.ScreenUpdating = False
startRow = Selection.Cells(1).Row
lr = startRow + Selection.Rows.Count - 1
For Each RngArea In Rng.Areas
Set sortRng = RngArea.Resize(, 2)
sortRng.Sort key1:=sortRng.Cells(1), order1:=xlAscending, Header:=xlNo
Next RngArea
For i = lr To startRow Step -1
If Cells(i, 2) = Cells(i - 1, 2) Then
Rows(i).Delete
End If
Next i
Application.ScreenUpdating = True
End Sub
I have data in a table, where I compare two columns J and T. The values which J and T can take include A2B, APL, BGF, CMA, among others (see code).
If these values are equal, copy row i into the sheet which has the name of the cells just checked.
If these values are not equal, copy row i into the sheets which have the name of the cells just checked.
Example: Compare J2 and T2,
Suppose J2=T2=BGF then copy row 2 and paste into sheet("BGF")
Next, compare J3 and T3
Suppose J3=BGF and T3=CMA, copy row 3 and paste into sheet(BGF) and sheet(CMA)
Next, compare J4 and T4
Suppose J4=Nothing and T4=CMA, copy row 4 and paste into sheet CMA
the only other combination is where Ji has a value and Ti is empty.
Problem: When running this code, If J3=BGF and T3= nothing (its empty), then the line is not copied to any sheet.
Here's the code
Sub Sortdata()
'step 1 clear all data
Sheets("A2B").Cells.ClearContents
Sheets("APL").Cells.ClearContents
Sheets("BGF").Cells.ClearContents
Sheets("CMA").Cells.ClearContents
Sheets("K Line").Cells.ClearContents
Sheets("MacAndrews").Cells.ClearContents
Sheets("Maersk").Cells.ClearContents
Sheets("OOCL").Cells.ClearContents
Sheets("OPDR").Cells.ClearContents
Sheets("Samskip").Cells.ClearContents
Sheets("Unifeeder").Cells.ClearContents
Dim i As Long
Dim ws1 As Worksheet
Dim ws2 As Worksheet
Dim LastRow As Long
With Worksheets("All Data")
LastRow = .Cells(.Rows.Count, 1).End(xlUp).Row
For i = 2 To LastRow
If IsEmpty(.Range("J" & i)) Then
Set ws1 = Nothing
Else
Set ws1 = Worksheets(.Range("J" & i).Value)
End If
If IsEmpty(.Range("T" & i)) Then
Set ws2 = Nothing
Else
Set ws2 = Worksheets(.Range("T" & i).Value)
End If
If ws1 Is Nothing Then
If Not ws2 Is Nothing Then
CopyToWs ws2, .Rows(i)
End If
ElseIf ws2 Is Nothing Then
If Not ws1 Is Nothing Then
CopyToWs ws1, .Rows(i)
End If
Else
CopyToWs ws1, Rows(i)
If ws1.Name <> ws2.Name Then
CopyToWs ws2, .Rows(i)
End If
End If
Next
End With
End Sub
Sub CopyToWs(ws As Worksheet, rng As Range)
rng.Copy
ws.Cells(ws.Rows.Count, 1).End(xlUp).Offset(1, 0).EntireRow.PasteSpecial xlPasteValuesAndNumberFormats
End Sub
Please try this code. It takes a slightly different approach to what you tried but it gets the job done, I think.
Option Explicit
Sub Sortdata()
' Variatus #STO 20 Jan 2020
Const WsNames As String = "A2B,APL,BGF,CMA,K Line,MacAndrews," & _
"Maersk,OOCL,OPDR,Samskip,Unifeeder"
Dim WsS As Worksheet ' Source
Dim Ws As Worksheet
Dim Rng As Range
Dim Rt As Long ' target row
Dim LastRow As Long
Dim J As Long, T As Long
Dim Tmp As Variant, PrevTmp As Variant
Dim R As Long, C As Long
'step 1 clear all data
Tmp = Split(WsNames, ",")
For R = LBound(Tmp) To UBound(Tmp)
On Error Resume Next
Worksheets(Tmp(R)).Cells.ClearContents
Next R
Application.ScreenUpdating = False
Set WsS = Worksheets("All Data")
With WsS
J = .Columns("J").Column
T = .Columns("T").Column
LastRow = .Cells(.Rows.Count, "A").End(xlUp).Row
For R = 2 To LastRow
PrevTmp = ""
For C = J To T Step T - J
Tmp = .Cells(R, C).Value
If Len(Tmp) And Tmp <> PrevTmp Then
On Error Resume Next
Set Ws = Worksheets(Tmp)
If Err = 0 Then
Set Rng = .Range(.Cells(R, 1), .Cells(R, .Columns.Count).End(xlToLeft))
With Ws
Rt = Application.Max(.Cells(.Rows.Count, "A").End(xlUp).Row + 1, 2)
Rng.Copy Destination:=Ws.Cells(Rt, 1)
End With
End If
End If
PrevTmp = Tmp
Next C
If R Mod 25 = 0 Then Application.StatusBar = "Currently processing row " & R
Next R
End With
With Application
.ScreenUpdating = True
.StatusBar = "Done"
End With
End Sub
I think you will be able to find your way through it and make any required modifications. Let me know if you require any assistance.
I'm trying to build a macro which will iterate through Activeworkbook/Activeworksheet& Range("A1:" & LastColumn & LastRow) and merge all duplicates in each column. The best starting point I could find was this post --> fastest way to merge duplicate cells in without looping Excel
But, like the OP comments on #PEH's answer https://stackoverflow.com/a/45739951/5079799 I get the following error Application defined error on the line Set R = .Range(Join(arr, ",")).
Does anybody have the fix and/or a better/alternative way to merge duplicates in a column?
Code from answer:
Sub MergeCellsNew()
Application.DisplayAlerts = False
Dim n As Name
Dim fc As FormatCondition
Dim Rng As Range, R As Range
Dim lRow As Long
Dim I&, J&
Dim arr As Variant
ReDim arr(1 To 1) As Variant
With ThisWorkbook.Sheets("tst")
Set Rng = .Range("A2:D11")
lRow = Rng.End(xlDown).Row
For J = 1 To 4
I = 2 'I = Rng.Row to automatically start at the first row of Rng
Do While I <= lRow
Set R = .Cells(I, J) 'remember start cell
'run this loop as long as duplicates found next to the start cell
Do While Trim(UCase(.Cells(I, J))) = Trim(UCase(.Cells(I + 1, J)))
Set R = R.Resize(R.Rows.Count + 1) 'and resize R + 1
I = I + 1
Loop
'now if R is bigger than one cell there are duplicates we want to add to the arr
'this way single cells are not added to the arr
If R.Rows.Count > 1 Then
arr(UBound(arr)) = R.Address
ReDim Preserve arr(1 To UBound(arr) + 1)
End If
I = I + 1
Loop
Next J
ReDim Preserve arr(1 To UBound(arr) - 1)
Set R = .Range(Join(arr, ","))
With R
.Merge
.HorizontalAlignment = xlCenter
.VerticalAlignment = xlCenter
End With
Stop
End With
Application.DisplayAlerts = True
End Sub
The problem I see with the methods outlined above is that it relies on duplicate data existing in an adjacent cell in the column. What if the duplicates are scattered in the column?
Here's an example where each column is examined by creating a Dictionary of all the values. Since each value must be unique (as the Key), then duplicates are removed with a list of just the unique Keys. Then it's just a matter of clearing the column of the previous data and copying the unique data back to the worksheet.
Option Explicit
Sub RemoveColumnDupes()
Dim lastCol As Long
With Sheet1
lastCol = .Cells(1, .Columns.Count).End(xlToLeft).Column
Dim c As Long
For c = 1 To lastCol
Dim columnDict As Dictionary
Set columnDict = CreateColumnDictionary(Sheet1, c)
If columnDict is Nothing then Exit For
'--- clear the existing data and replace with the cleaned up data
.Range("A1").Offset(, c - 1).Resize(.Rows.Count, 1).Clear
.Range("A1").Offset(, c - 1).Resize(columnDict.Count, 1) = KeysToArray(columnDict)
Next c
End With
End Sub
Private Function CreateColumnDictionary(ByRef ws As Worksheet, _
ByVal colIndex As Long) As Dictionary
Dim colDict As Dictionary
Dim lastRow As Long
With ws
lastRow = .Cells(.Rows.Count, colIndex).End(xlUp).Row
If lastRow = 1 Then
'--- can't create a dictionary with no data, so exit
Set colDict = Nothing
Else
Set colDict = New Dictionary
Dim i As Long
For i = 1 To lastRow
If Not colDict.Exists(.Cells(i, colIndex).Value) Then
colDict.Add .Cells(i, colIndex).Value, i
End If
Next i
End If
End With
Set CreateColumnDictionary = colDict
End Function
Private Function KeysToArray(ByRef thisDict As Dictionary) As Variant
Dim newArray As Variant
ReDim newArray(1 To thisDict.Count, 1 To 1)
Dim i As Long
For i = 1 To thisDict.Count
newArray(i, 1) = thisDict.Keys(i - 1)
Next i
KeysToArray = newArray
End Function
While I don't know the issue with the code I found and posted in OP. I did find awesome solutions on https://www.extendoffice.com and modified it to suit my needs as found below.
Test:
Sub MergeTest()
Dim wsrng As Range
Set wsrng = ActiveSheet.UsedRange
Call MergeWS(wsrng)
'Call UnMergeWS(wsrng)
End Sub
Merge:
https://www.extendoffice.com/documents/excel/1138-excel-merge-same-value.html
Function MergeWS(WorkRng As Range)
Dim Rng As Range, xCell As Range
Dim xRows As Integer
Application.ScreenUpdating = False
Application.DisplayAlerts = False
xRows = WorkRng.Rows.Count
For Each Rng In WorkRng.Columns
For i = 1 To xRows - 1
For j = i + 1 To xRows
If Rng.Cells(i, 1).Value <> Rng.Cells(j, 1).Value Then
Exit For
End If
Next
With WorkRng.Parent.Range(Rng.Cells(i, 1), Rng.Cells(j - 1, 1))
.Merge
.HorizontalAlignment = xlCenter
.VerticalAlignment = xlCenter
End With
i = j - 1
Next
Next
Application.DisplayAlerts = True
Application.ScreenUpdating = True
End Function
UnMerge:
https://www.extendoffice.com/documents/excel/1139-excel-unmerge-cells-and-fill.html
Function UnMergeWS(WorkRng As Range)
Dim Rng As Range, xCell As Range
xTitleId = "KutoolsforExcel"
Application.ScreenUpdating = False
Application.DisplayAlerts = False
For Each Rng In WorkRng
If Rng.MergeCells Then
With Rng.MergeArea
.UnMerge
.Formula = Rng.Formula
End With
End If
Next
Application.DisplayAlerts = True
Application.ScreenUpdating = True
End Function
https://www.freesoftwareservers.com/display/FREES/Merge+and+UnMerge+cells+-+Excel+VBA
I'm trying to compare sheet1 "A" column values to sheet2 "E:E" column values and copy/paste the whole line of every match to sheet3. Please help me to complete this task. I'm very new to VBA.
Thank you very much in advance!
Sub DelDups_TwoLists()
Dim iListCount As Integer
Dim iCtr As Integer
' Turn off screen updating to speed up macro.
Application.ScreenUpdating = False
' Get count of records to search through (list that will be deleted).
iListCount = Sheets("sheet1").Cells(Rows.Count, "A").End(xlUp).Row
' Loop through the "master" list.
For Each x In Sheets("Sheet2").Range("E:E" & Sheets("Sheet1").Cells(Rows.Count, "A").End(xlUp).Row)
' Loop through all records in the second list.
For iCtr = iListCount To 1 Step -1
' Do comparison of next record.
' To specify a different column, change 1 to the column number.
If x.Value = Sheets("Sheet1").Cells(iCtr, 1).Value Then
' If match is true then delete row.
Sheets("Sheet1").Cells(iCtr, 1).EntireRow.Copy
Sheets("Sheet3").Select.Paste
End If
Next iCtr
Next
Application.ScreenUpdating = True
MsgBox "Done!"
End Sub
Sub DelDupsTwoLists()
Dim lastRowWs1 As Long, lastRowWs2 As Long
Dim ws1 As Worksheet, ws2 As Worksheet, ws3 As Worksheet
Set ws1 = Worksheets(1)
Set ws2 = Worksheets(2)
Set ws3 = Worksheets(3)
lastRowWs1 = LastRow(ws1.Name, 1)
lastRowWs2 = LastRow(ws2.Name, 5) 'E = 5
Dim myCell1 As Range, myCell2 As Range
Dim ws1Range As Range, ws2Range As Range
Set ws1Range = ws1.Range(ws1.Cells(1, "A"), ws1.Cells(lastRowWs1, 1))
Set ws2Range = ws2.Range(ws2.Cells(1, "E"), ws2.Cells(lastRowWs2, 1))
Dim rangeToDelete As Range
For Each myCell1 In ws1Range
For Each myCell2 In ws2Range
If myCell1.Value = myCell2.Value Then
Dim lastRowWs3: lastRowWs3 = LastRow(ws3.Name, 1) + 1
myCell2.EntireRow.Copy Destination:=ws3.Cells(lastRowWs3, 1)
If Not rangeToDelete Is Nothing Then
Set rangeToDelete = Union(rangeToDelete, myCell2.EntireRow)
Else
Set rangeToDelete = myCell2.EntireRow
End If
End If
Next
Next
If Not rangeToDelete Is Nothing Then
Debug.Print "Deleting rangeToDelete - "; rangeToDelete.Address
rangeToDelete.Delete
End If
Debug.Print "Done!"
End Sub
Public Function LastRow(wsName As String, Optional columnToCheck As Long = 1) As Long
Dim ws As Worksheet
Set ws = Worksheets(wsName)
LastRow = ws.Cells(ws.Rows.Count, columnToCheck).End(xlUp).Row
End Function
Pretty much I rewrote the whole code from scratch. It pretty much uses the initial n2 complexity, but is rather faster than that, because the deletion of the rows in WorkSheet(2) is done in a single last step rangeToDelete.Delete, which saves a lot of time.
Pretty much, the code defines 2 ranges with which is works - ws1Range and ws2Range, using the LastRow function. Once it defines them, it starts looping through them and comparing them. Hence the n2 complexity. In case of equal values, the row is copied and the cell is added to the rangeToDelete.
Note - it will probably not work as "out of the box solution", but try to debug further with F8 and see what happens.
Additionally:
Using Integer is not a great idea in VBA.
"_" in the Sub name is used for Events in VBA, thus it is not a great idea to use it. (although it works)
How to avoid using Select in Excel VBA
Give this a try (see comments in code for more details):
Sub DelDups_TwoLists()
' Turn off screen updating to speed up macro.
Application.ScreenUpdating = False
With ActiveWorkbook
Dim wsSrc As Worksheet: Set wsSrc = .Sheets("Sheet1") 'declare and set the source worksheet
Dim wsDst As Worksheet: Set wsDst = .Sheets("Sheet3") 'declare and set the destination worksheet
Dim R1 As Long, R2 As Long, C As Long, lRow As Long, lCol As Long 'declare variables to use
With wsSrc
lCol = .Cells(1, Columns.Count).End(xlToLeft).Column 'get the last column value in the source sheet, at row 1, will reuse this laster
Dim arrData_1 As Variant: arrData_1 = .Range(.Cells(1, 1), .Cells(.Cells(Rows.Count, 1).End(xlUp).Row, 1)) 'declare and allocate the source data to an array
End With
With .Sheets("Sheet2")
Dim arrData_2 As Variant: arrData_2 = .Range("E1:E" & .Cells(Rows.Count, 1).End(xlUp).Row) 'declare and allocate the compare data to an array
End With
End With
With wsDst
For R1 = LBound(arrData_1) To UBound(arrData_1) 'for each row in the source data
For R2 = LBound(arrData_2) To UBound(arrData_2) 'for each row in the compare data
If arrData_1(R1, 2) = arrData_2(R2, 1) Then 'if there is a match
lRow = .Cells(Rows.Count, 1).End(xlUp).Row + 1 'get the last row in the destination sheet
.Range(.Cells(lRow, 1), .Cells(lRow, lCol)).Value = _
wsSrc.Range(wsSrc.Cells(R1, 1), wsSrc.Cells(R1, lCol)).Value 'allocate the matching values
Exit For 'exit early here if there is a match, go to next row to check
End If
Next R2
Next R1
End With
Application.ScreenUpdating = True
MsgBox "Done!"
End Sub
I'm close to figuring out the correct way to do this but I'm all searched out. I'm trying to search a column (Asset Tag) that holds various garbage but want to keep entire rows that start with AA as this confirms the row is actually an Asset Tag.
The code below gives me an "Object Required" error, and I believe I might not be correctly telling it to look at cell values with the"If rng.Cells(i) <> Left(cell.Value, 2) = "AA" Then" statement. Can someone point me in the right direction of what I need to do?
Sub DeleteRows()
Dim rng As Range
Dim i As Double, counter As Double
Set rng = Range("C:C")
i = 1
For counter = 1 To rng.Rows.count
If rng.Cells(i) <> Left(cell.Value, 2) = "AA" Then
rng.Cells(i).EntireRow.Delete
Else
i = i + 1
End If
Next
End Sub
Thanks!
If you're deleting rows then you should always work from the bottom up:
Sub DeleteRows()
Dim rng As Range
Dim counter As Long, numRows as long
With ActiveSheet
Set rng = Application.Intersect(.UsedRange, .Range("C:C"))
End With
numRows = rng.Rows.Count
For counter = numRows to 1 Step -1
If Not rng.Cells(counter) Like "AA*" Then
rng.Cells(counter).EntireRow.Delete
End If
Next
End Sub
Given your code took 5 minutes I suggest you try this AutoFilter approach. It uses a working column (the first unused column part the usedrange) to run a case sensitive text for the first two letters of column C cells being "AA". If not an autofilter deletes these rows.
Sub KillnonAA()
Dim ws As Worksheet
Dim rng1 As Range
Dim lRow As Long
Dim lCol As Long
Set ws = ActiveSheet
'in case the sheet is blank
On Error Resume Next
lRow = ws.Cells.Find("*", , xlValues, , xlByRows, xlPrevious).Row
lCol = ws.Cells.Find("*", , xlValues, , xlByColumns, xlPrevious).Column
On Error GoTo 0
If lRow = 0 Then Exit Sub
Set rng1 = ws.Range(ws.Cells(1, lCol + 1), ws.Cells(lRow, lCol + 1))
Application.ScreenUpdating = False
Rows(1).Insert
With rng1.Offset(0, 1)
.FormulaR1C1 = "=EXACT(LEFT(RC3,2),""AA"")"
.AutoFilter Field:=1, Criteria1:="FALSE"
.EntireRow.Delete
'in case all records were deleted
On Error Resume Next
.EntireColumn.Delete
On Error GoTo 0
End With
Application.ScreenUpdating = True
End Sub