I am currently trying to remove rows from column A to column V based on duplicates found in column A.
my current formula is:
Workbooks(TgtWB).ActiveSheet.Range("A15:V500").RemoveDuplicates _
Columns:=Array(1), Header:=xlYes
My header is located on row 15.
the error i am getting is
Application-defined or object-defined error
I have tried switching ActiveSheet with worksheet("xxx") but still doesn't seem to work either. i'm not sure what i'm doing wrong here.
Try (note worksheetS, not worksheet),
Workbooks(TgtWB).worksheetS("xxx").Range("A15:V500").RemoveDuplicates Columns:=1, Header:=xlYes
'or,
ActiveSheet.Range("A15:V500").RemoveDuplicates Columns:=1, Header:=xlYes
If TgtWB is open, it may or may not contain the ActiveSheet. Each open workbook does not have an ActiveSheet. There is only one ActiveSheet for the application instance. ActiveSheet is not a property of the Workbooks collection.
Using built in functionality is great, unless you're a beginner like myself and the .Applications and .Worksheets get overwhelming and confusing. For a small data set, such as columns A through V (depending obviously on how many rows you have), a loop and if-statement not only can work well, but can be good practice as well! Something like this may work well:
Dim CurrentValue, LastValue As Variant
LastValue = ""
For I = 1 To 500
CurrentValue = Sheets("Sheet 1").Range("A" & i).Value
If CurrentValue = LastValue Then
Sheets("Sheet 1").Range("A" & i & ":V" & i).Clear Contents
Else
LastValue = CurrentValue
End If
Next i
This will loop through every row, ask if the current value in the cell is the same as the one previously observed (except the first one, the first one will automatically be saved by nature), and if it has been, it will clear the contents of the row. If it's not the same, it will assign it to the new variable and begin looking for new ones.
There are two drawbacks with this method that can be solved by simply adapting the code to your needs:
Non-Sequential Items: If the values that are duplicates are not sequential, the code won't kick them out. At that point I recommend using an application code or a dictionary. Dictionaries are infinitely useful for storing unique data entries and skipping duplicates.
Deleting Rows: When you delete rows in a loop it messes up your loop, throwing off the order of your incrementer. The best way to combat this is by first clearing the columns then having your code loop for empty rows and delete them outside of your loop.
This is very basic but something I've found greatly helpful as a beginner and hopefully other beginners and yourself can learn something from it.
Happy Coding!
Related
At my job I have automated a very manual task and to the point they have wanted to expand it to the other department where they work a little differently. So the goal of this is I want to be able to filter column "A" and then filter another column based off of phrases that I already have in place as well. The data in column "A" I would have a source in another sheet, but it would have around potentially 200-400 possibilities to look for. After it filters column A I then want it go to column "AG" and then do another loop filtering based off of provided key phrases that the analyst would select based on a data validation. Once it filters those two criteria I then have the codes in place to generate the spreadsheets for the analyst. The code below is an example of the first block, I have 4 other codes that are pretty much the same they just generate different templates, I had to do multiple codes cause I didn't know how to do a loop based off of a source.
Sub Generate_Consolidated_Update_Reports()
Dim EDI As String
EDI = Environ("USERPROFILE") & "\desktop\foldername\foldername2\foldername3\filename " &
Format(Now(), "MM.DD.YY")
Workbooks("Master(where the filtering happens)").Activate
'The next line of code I am just doing the second filtering which is based off of a data validation
'I created, essentially the analyst would just select a key phrase and then that would
'prompt the code to generate the template, I haven't figured out how to do it based off of
'the source that's why I just have the specific name in the key range
'The first step to the code would need to be looking for anything in column "A", but like
'I said that could be anywhere from 200-400 possibilities. I have access to it, though and have
'it listed in "Sheet2" along with the phrases in column "AG" as well.
ActiveWorkbook.Worksheets("Master").Sort.SortFields.Add Key:=Range("AG:AG" _
), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
With Selection
.autofilter Field:=33, Criteria1:="Send to EDI Team - Account Level"
.Offset(1, 0).Select
End With
'The next line of code will be seeing if it is empty, I realized that it would work once,
'but if there ever was data the next line of looking for empty would always
'just filter anyways and keep creating the template even though there was no data
'so I did this route where if it saw it was empty I called another code, would be awesome
'if I could figure that out too!
'This then goes on repeat down the line, it's 5 different codes, so it isn't clean. :/
'so I only did one so you wouldn't see a ton of fluff!
Dim Lr As Long
Lr = Range("A" & Rows.Count).End(xlUp).Row
If Lr > 1 Then
Workbooks.Add
ActiveWorkbook.SaveAs EDI & ".XLS"
Sheets("Sheet1").Select
Sheets("Sheet1").Name = "EDI Account Update"
'Redacted code, just fluff on creating the template for the analyst
'Next line is just doing the code to show all the data again
'Then within that if statement to then call another sub that is essentially the same process
'If the code doesn't find it it goes to else where it then just calls the other sub
If ActiveSheet.AutoFilterMode Then ActiveSheet.ShowAllData
Range("A1").Select
Call Create_EDI_Update_GroupLevel
Else
ActiveSheet.ShowAllData
Range("A1").Select
Call Create_EDI_Update_GroupLevel
End If
End Sub
The reason I have to also call other subs is because each criteria they select will generate a completely different template based on company policies and such.
Really sorry if this isn't clear, I am still learning coding and just having a hard time trying to explain exactly what I needed. Thank you so much for your time.
I wanted to add a comment but not enough reputation yet.
One question I have is: Are you filtering on one phrase among 200-400 or can it be multiple?
I can't be sure, however an Advanced Filter might help you in this case. It is not possible for me to go into all the details. There is a very good tutorial on Youtube about this (where I learned myself): VBA Advanced Filter - The FASTEST way to Copy and Filter Data
You can also use "Worksheet_Change" event to fill in the filter. Couple it with:
If Not Application.Intersect(Target,rngRange) is Nothing Then
' Your code here
' Where rngRange is a Range object that you want the change event to catch
End If
Another note is, you can use (considering data starts at "A1") Range("A1").CurrentRegion if you don't have any completely empty rows within your data instead of .End(xlUp). Actually you can use CurrentRegion in any cell that is inside the data range. Check out the behaviour by pressing CTRL + * after selecting a cell.
Dim rngData as Range
Set rngData = ws.Range("A1").CurrentRegion
' ws can be a worksheet object you can set alike range above or worksheet codename
' or Worksheet("SheetName")
' Then rngData.Row will give the top row of rngData
' rngData.Column will give you you the leftmost column
' rngData.Rows.Count will give the number of rows
' rngData.Columns.Count will give the number of columns
' rngData.resize() and rngData.Offset() helps a lot too, can be used in same line
' i.e., rngData.offset(2,0).resize(1,1) : move the range 2 rows down, resize to 1 row,1 column
' Do whatever with rngData
' After you are done clean up, doesn't matter most of the time in VBA but this is better
' from what I have read/learned from others
Set rngData = Nothing
This may not be the exact answer you are looking for, but may get you started. The video is 20 minutes long, see if it can be used in your case.
Edit 1: To further clarify
rngData part is more a general approach. You haven't said anything about being able to use Advanced Filtering but here how I would do it.
Assume, we have 3 sheets. wsData, wsFilter, wsReport.
wsData would be the sheet where data is entered.
wsFilter would only hold the filter criteria.
wsReport would be the sheet that you will export.
wsData, as an example assume row 1 is the header row.
wsFilter would be only 2 columns (with headers corresponding to A and AG), corresponding to Column A and AG.
On wsReport, you only have to clear contents and enter the column headers from wsData that you would want to appear on the customized report.
You will need 3 ranges, 1 for each worksheet. i.e., rngData, rngFilter, rngReport
The code to creating the report is easy as:
rngData.AdvancedFilter xlFilterCopy, rngFilter, rngReport
(I won't get into how to decide on the ranges as the video does it better than I would be able to).
Then next step should be: How to enter the filter criteria?
I have an excel where I have 2 sheets and I want to import selected columns to another sheet and delete possible duplicate rows. The problem is, that when I run my code, it deletes my latest duplicate row, eventhough I want to save latest row and delete the other 'older' duplicate. I really appreciate any help! Thank you :) Here is my code what I have tried so far:
Private Sub CommandButton1_Click()
Cells.RemoveDuplicates Columns:=Array(1, 2, 3)
Dim lastrow As Long, erow As Long
lastrow = Worksheets("one").Cells(Rows.Count, 1).End(xlUp).Row
For i = 2 To lastrow
Worksheets("one").Cells(i, 1).Copy
erow = Worksheets("two").Cells(Rows.Count, 1).End(xlUp).Row
Worksheets("one").Paste Destination:=Worksheets("two").Cells(erow + 1, 1)
Worksheets("one").Cells(i, 3).Copy
Worksheets("one").Paste Destination:=Worksheets("two").Cells(erow + 1, 2)
Next i
End Sub
Another question is, I have tried this code for test excel, but where I want to use this macro has a lot larger data. It takes a very long time to go through all the data.. is the problem in my code or is the actual excel just so big? I hope I explain everything crearly.
Thank you!
"The problem is, that when I run my code, it deletes my latest duplicate row..."
The problem is the sort order of your data.
Remove Duplicates moves from first index to last and and keeps the first occurrence. So you either need to sort your data such that your "oldest" duplicate is the first occurrence at top or you have to use a different method to remove duplicates.
"is the problem in my code or is the actual excel just so big?"
It could be both, but one thing is certain, your code can definitely run faster.
The low hanging fruit is to put Application.EnableScreenUpdating = False in the beginning of you sub. That stops Excel from updating the screen when your runs. It will make a huge difference. Just remember to set it back to True again.
You're also looping through a range one row at a time but I don't see any particular reason why you need to. You can copy the while range in a single statement without looping. You don't even need to copy of you don't care about ordering the format, you could transfer the values just make the ranges equal each other.
An example of how you to transfer values without cutting or looping
Worksheets("two").Range("A2:A" & lastrow).Value = Worksheets("one").Range("E2:E" & lastrow).Value
You can use the same approach with copy and paste if you prefer.
I am trying to have this piece of code clear the contents of a certain range, before I bring new data. This range has a header right in the above row, which is getting cleared as well, although the range doesn't include it.
I've tried specifying the range and having other cells selected before running it, but the header gets cleared anyway.
With MyResults
LastRowResults = Range("A16:K" & Rows.Count).End(xlUp).Row
.Range("A$16:$K$" & LastRowResults).Select
Selection.SpecialCells(xlCellTypeConstants, 23).Select
Selection.ClearContents
It's clearing row 15 and even the content of cell A9, some of the times I run it.
The goal is to have this range cleared, keeping formulas in some columns intact, so that filtered data down the road can be brought in, making up the report required.
Avoiding Select and Activate in VBA does miracles in performance and coding abilities (How to avoid using Select in Excel VBA). Concerning your code, try to avoid the header, if it is a default at line 16:
With MyResults
LastRowResults = .Range("A17:K" & Rows.Count).End(xlUp).Row
MsgBox LastRowResults 'Consider removing this line, once the code works.
Dim deleteArea As Range
Set deleteArea = .Range("A$17:$K$" & LastRowResults)
deleteArea.ClearContents
End With
In the OP code, there was a lack of . before the Range in this line:
LastRowResults = .Range("A17:K" & Rows.Count).End(xlUp).Row
Thus, the lastRowResults was getting its value from the ActiveSheet or the Sheet in which the code resides (if not in a module). The . is quite important in the With MyResult.
In general, when the last row is searched for multiple columns, then one should define exactly the expected output. E.g., the for the biggest last row, something like this is way better:
LastRowResults = .Columns("A:K").Find(What:="*", SearchDirection:=xlPrevious, SearchOrder:=xlByRows).Row
I'm making a macro that is supposed to check for a set of values in an input list from the user. If the values from my source list are NOT in the user input, they have to be copied into a different spreadsheet. The macro runs without errors, but it won't filter any results. It just copies everything.
This is the code:
Sub CheckRow()
For i = 2 To Application.CountA(Worksheets("Source").Range("A:A")) 'Loop through rows
Set rgFound = Worksheets("Input").Range("A:A").Find(Worksheets("Source").Range("A" & i).Value, LookAt:=xlWhole) 'Find the value from the source list in the Input List
If rgFound Is Nothing Then 'If there is no match it goes to output
Worksheets("Output").Range("A" & Application.CountA(Worksheets("Output").Range("A:A")) + 1).Value = Worksheets("Source").Range("A" & i).Value 'Copy the value beow any existing values
End If
Next i
End Sub
I will appreciate any suggestions you have to offer.
Your condition should be If Not rgFound Is Nothing Then (the Not is missing in your code). Basically, the Find method doesn't find anything and therefore everything is copied.
I also advise you to use Option Explicit at the top of your code sheet. This would alert you to the use of the variable j in Find(Worksheets("Source").Range("A" & j). I think you are looking for A & i. However, since j appears to have a value of 0 your code shouldn't copy anything because there is no row 0. Using Option Explicit would force you to declare all variables, eliminating guessing games like this one when reading your code. Since you are the one to read it most of the time you would also be the prime beneficiary of the improvement.
Fixed. It seems that the Find method thinks that Value and ="Value" are two different things. Once I changed all the formula cells to plain text my code worked like a charm.
I would like to know if there is a faster way do this than the code I am using. I got the code using xlUp from the recorder.
rCnt = Cells(Rows.Count, "B").End(xlUp).Row
ActiveSheet.Range("$B$1:$J" & rCnt).AutoFilter Field:=5, _
Criteria1:=Application.Transpose(arrCodes), Operator:=xlFilterValues
Rows("2:" & rCnt).Delete Shift:=xlUp
And actually, if there was some way to flip the filter, I wouldn't need to delete at all as this is a temporary table that I copy from. However, all my research has failed to find a way to do
Criteria1:=Application.Transpose(<>arrCodes)
and arrCodes has too many elements to list in the filter. And the stuff that is not in arrCodes is way too numerous to make an array from. Thanks.
If you want to just use Excel UI and not formulas or VBA, you can do the following simple steps to get an "inverse" filter. This could then be ported to VBA if needed:
Apply the filter with the opposite conditions
Color those cells in one column (either font or background)
Clear the filter
Filter again but this time by cells in that column without color
Copy those results where you want them
This will not work well if the column already has some background colors. If that is the case, you can add a new column and color it. If this is in VBA, you could automate those steps. There are limits, but this is quick and simple if it applies.
I've had success in the past with building then deleting a range. You can combine ranges with Union(). I've attached a bit of example code, it's not wonderful but it shows the basic concept. This example deletes rows with odd numbers in column A in rows 2 through 11.
Public Sub DeleteRows()
Dim deleteThis As Range
For i = 2 To 11
If Sheet1.Cells(i, 1).Value Mod 2 = 1 Then
If deleteThis Is Nothing Then
Set deleteThis = Sheet1.Rows(i)
Else
Set deleteThis = Union(deleteThis, Sheet1.Rows(i))
End If
End If
Next i
deleteThis.Delete xlShiftUp
End Sub