Application defined or object defined error on code - vba

I get an error on this line in my code, any ideas what the issue may be?
Intersect(.UsedRange, .UsedRange.Offset(1)).SpecialCells(12).EntireRow.Delete
Here is the rest of the code:
Sub DefineDL_IDL()
Dim wbTHMacro As Workbook, wsRegulares As Worksheet, wsRegularesDemitidos As Worksheet, wsTempActivos As Worksheet, _
wsTempJA As Worksheet, wsTempFit As Worksheet, wsTempDemitidos As Worksheet, wsPS As Worksheet, wsResultados As Worksheet, _
wsDLList As Worksheet, wssheet As Worksheet, count_DL As Integer, count_IDL As Integer
Dim x&, r As Long
'*************REGULARES***********
Sheets("Regulares").Select
'Debug.Print xlToRight
'Sheets("Raw").Copy before:=Sheets(2)
With Sheets("Regulares")
'.Name = "Final2"
.UsedRange.AutoFilter 9, "INATIVE"
Intersect(.UsedRange, .UsedRange.Offset(1)).SpecialCells(12).EntireRow.Delete
r = WorksheetFunction.CountA(.Range("A:A"))
.UsedRange.AutoFilter
.Range("J:J").Insert xlToRight
.Range("J1").Value = "Real MO"
.Range("K:K").Cut
.Range("I:I").Insert xlToRight
.Range("Q:Q").Cut
.Range("I:I").Insert xlToRight
.Range("L2:L" & r).FormulaR1C1 = "=VLOOKUP(RC[-3],'DL List'!C[-11]:C[-10],2,0)"
.Range("L2:L" & r).Value = .Range("L2:L" & r).Value
For x = 2 To r
If Range("L" & x).Text = "#N/A" Then
'If Range("K" & x).Value = "DL" Then
' Range("L" & x).Value = "DL"
'Else: Range("L" & x).Value = "IDL": End If
Range("L" & x).Value = "IDL"
End If
Next x
End With
count_DL = Application.WorksheetFunction.CountIf(ActiveSheet.Range("L:L"), "DL")
count_IDL = Application.WorksheetFunction.CountIf(ActiveSheet.Range("L:L"), "IDL")
Worksheets("Resultados").Range("B17") = count_DL
Worksheets("Resultados").Range("C17") = count_IDL

Your expression works on my test worksheet so the problem must be something about your data.
I do not like stringing properties together like this because the objective becomes very unclear. Worse, if it fails, you do not know where is fails.
Try replacing the statement with this:
Dim rng As Range
Debug.Print .UsedRange.Address
Debug.Print .UsedRange.Offset(1).Address
Set rng = Intersect(.UsedRange, .UsedRange.Offset(1))
Debug.Print rng.Address
Debug.Print rng.SpecialCells(12).Address
Debug.Print rng.SpecialCells(12).EntireRow.Address
rng.SpecialCells(12).EntireRow.Delete
Step through this code to make sure each range is as you expect.
My guess that that there are no visible cells in the range so you are attempting to delete Nothing.
Edit Extra information about finding last row of worksheet.
There are a variety of methods of finding the last used row or column of a worksheet. None work in every situation but UsedRange is the method least likely to give the result you expect.
The most popular method of finding the last row, judging by answers here, is:
RowLast = .Cells(Rows.Count,9).End(xlUp).Row
This is the VBA equivalent of placing the cursor in the bottom cell of column 9 and clicking Ctrl+Up. RowLast will be set to the last row with a value in column 9 unless you have a value in the bottom cell. For this method to be of any use, there must be a value in the specified column of the last used row.
Find is a reliable method of finding the last value by either row or column.
SpecialCells is another useful method.
This answer of mine VBA Dynamic Ranges includea a macro, FindFinal, which demonstrates how these methods can fail to give the result you might expect. If you wish to fully understand the issues, step through this macro studying what happens.

Related

Creating a macro that properly filters data and puts it on another sheet

I have a large dataset that is ordered in a weird way, as in the picture:
This is how my data looks currently
This is what i want it to be like
So mainly I want to do 2 things, first i want to cut the two other columns that display data, and paste them underneath the first column, but only for the first weeks period, and then sort the data, macro recording doesn't work very well since weeks are really months, therefore the amount of days changes per month, hence the height of each column.
My idea is to use a while loop to scroll through the first column (the first one displaying "Day", for each non-number entry (say the first no-greater than zero input), and then cut the whole three block array and paste it somewhere else, say a new sheet called Week "n", given it's the n'th week.
Then properly order this array, copying the two right blocks underneath the first one, and sort them by day and hour.
This I want to do for each data period of a week, but I'm not that well versed on vba's syntax to achieve this, mostly i do not know how to order the array the way im looking to once they are copied to new sheets, neither do i know how to do it if i were not to add new sheets and instead reformat it in place.
Any help is welcome.
Considering your data is set up as per the following image...
Place the following code on a Standard Module like Module1...
Sub TransformWeekData()
Dim sws As Worksheet, dws As Worksheet
Dim lr As Long, dlr As Long, i As Long
Dim Rng As Range
Application.ScreenUpdating = False
Set sws = Sheets("Sheet1") 'Source data sheet
lr = sws.Cells(Rows.Count, 1).End(xlUp).Row
On Error Resume Next
Set dws = Sheets("Combined Data") 'Output Sheet
dws.Cells.Clear
On Error GoTo 0
If dws Is Nothing Then
Set dws = Sheets.Add(after:=sws)
dws.Name = "Combined Data"
End If
On Error Resume Next
For Each Rng In sws.Range("A2:A" & lr).SpecialCells(xlCellTypeConstants, 1).Areas
If dws.Range("A1").Value = "" Then
dlr = 1
Else
dlr = dws.Range("A" & Rows.Count).End(3)(2).Row
End If
dws.Range("A" & dlr).Value = Rng.Cells(1).Offset(-2, 0).Value
dws.Range("A" & dlr + 1 & ":C" & dlr + 1).Value = Array("Day", "Amount", "Hour")
For i = 1 To 9 Step 3
dlr = dws.Range("A" & Rows.Count).End(3)(2).Row
Rng.Offset(, i - 1).Resize(Rng.Cells.Count, 3).Copy dws.Range("A" & dlr)
Next i
Next Rng
dlr = dws.Range("A" & Rows.Count).End(xlUp).Row
For Each Rng In dws.Range("A2:A" & dlr).SpecialCells(xlCellTypeConstants, 1).Areas
Rng.Resize(Rng.Cells.Count, 3).Sort key1:=Rng.Cells(1), order1:=xlAscending, key2:=Rng.Cells(1, 3), order2:=xlAscending, Header:=xlNo
Next Rng
Application.ScreenUpdating = True
End Sub
The code above will insert a sheet called Combined Data if doesn't exist in the workbook with the data in the desired format as shown in the image below...
You may change the output sheet's name as per your requirement.

Copy row where cell matches worksheet name throws Subscript out of range (Error 9)

I was searching around this forum for quite a long time and learned quite a bit. However, I have a problem now which is easy to fix, I guess, but I am too blind to see the right solution.
I have a sheet with over 50k rows which also contain a number for suppliers, so these numbers happen to be duplicates.
I got a vba macro which creates a new sheet for every supplier number without duplicates, so thats not the problem.
However, I want to copy the data of the row to the worksheet which is equal to the supplier number appearing in that row.
The supplier numbers are in column A. So, if Row 2 has supplier number 10 then copy the row to sheet "10", Row 3 has number 14 to sheet "14", Row 4 has number 10 to sheet "10" again and so on.
I used the following code I found here and remodeld it a bit.
Sub CopyRows()
Dim DataSht As Worksheet, DestSht As Worksheet
Set DataSht = Sheets("All Data")
RowCount = DataSht.Cells(Cells.Rows.Count, "A").End(xlUp).Row
For i = 2 To RowCount
DataSht.Range("A" & i).EntireRow.Copy
Set DestSht = Sheets(DataSht.Range("A" & i).Value)
DestLast = DestSht.Cells(Cells.Rows.Count, "A").End(xlUp).Row
DestSht.Range("A" & DestLast + 1).Paste
Next i
End Sub
However it get an subscript out of range error on this line:
Set DestSht = Sheets(DataSht.Range("A" & i).Value)
Try this:
For i = 2 To RowCount
Set DestSht = Sheets(CStr(DataSht.Range("A" & i)))
DestLast = DestSht.Cells(Cells.Rows.Count, "A").End(xlUp).row
DataSht.Range("A" & i).EntireRow.Copy Destination:=DestSht.Range("A" & DestLast + 1)
Next I
Since:
with CStr function it points to Sheets("12")
while Cstr it'd point to Sheets(12), i.e. the twelfth sheet in the workbook, which could not be the one you'd want or neither be there.
This error is caused because Excel can't identify a sheet with the same name as your column A value. You might want to run this small sub to see if it gives you a clue as to why...
Sub SheetNamesAndIndexes()
DIm ws as Worksheet
For Each ws in ThisWorkbook.Sheets
Debug.print ws.Name & ";" & ws.Index
Next
End Sub
This will show you the names and the indexes of all your sheets. If that doesn't reveal the problem, you can take this and incorporate it into your code to help you debug, like so...
Dim ws as Worksheet
For i = 2 To RowCount
For Each ws in ThisWorkbook.Sheets
Debug.Print ws.Name * ";""" & DataSht.Range("A" & i).Value & """;" & ws.Name = DataSht.Range("A" & i).Value
Next
...
This will put the value of each cell in Col A next to each sheet name, along with whether or not Excel thought the two matched. If you see one that says "False" that you expect to be "True", investigate that next. I've put quotes around the DataSht.Range.Value to make it more obvious if you've got extra spaces, etc.
If that doesn't yield answers, another answer suggested making sure that you're not comparing strings to integers. If that's the case, then wrap your Range.Value in a Cstr() and run it again. Good Luck!

copying a row from one sheet to another automatically based on information in one column and sorted by dated (into Months)

Right, I'm having an issue that I'm hoping one (or more) of you will be able to help me with.
For a week now, I've been trying to work out how I can automatically copy and update rows from one sheet in my Workbook to separate sheets based on the month of a date in the second column.
I've tried everything I can think of, VLOOKUP doesn't seem to do it and I know little about VBA to be able to work out how it may work.
I did find a solution that looked promising Using VBA, that split all the varying rows based on the different values in one of the columns (I created an extra column and formatted it to text and then put JAN 15, FEB 15 etc.) then created new tabs and inserted the data into those. Unfortunately, for some reason this ended up creating excess tabs and wouldn't update the breakdown sheets when I changed the Master sheet.
The code I found was:
Sub parse_data()
Dim lr As Long
Dim ws As Worksheet
Dim vcol, i As Integer
Dim icol As Long
Dim myarr As Variant
Dim title As String
Dim titlerow As Integer
vcol = 1
Set ws = Sheets("Sheet1")
lr = ws.Cells(ws.Rows.Count, vcol).End(xlUp).Row
title = "A1:C1"
titlerow = ws.Range(title).Cells(1).Row
icol = ws.Columns.Count
ws.Cells(1, icol) = "Unique"
For i = 2 To lr
On Error Resume Next
If ws.Cells(i, vcol) <> "" And Application.WorksheetFunction.Match(ws.Cells(i, vcol), ws.Columns(icol), 0) = 0 Then
ws.Cells(ws.Rows.Count, icol).End(xlUp).Offset(1) = ws.Cells(i, vcol)
End If
Next
myarr = Application.WorksheetFunction.Transpose(ws.Columns(icol).SpecialCells(xlCellTypeConstants))
ws.Columns(icol).Clear
For i = 2 To UBound(myarr)
ws.Range(title).AutoFilter field:=vcol, Criteria1:=myarr(i) & ""
If Not Evaluate("=ISREF('" & myarr(i) & "'!A1)") Then
Sheets.Add(after:=Worksheets(Worksheets.Count)).Name = myarr(i) & ""
Else
Sheets(myarr(i) & "").Move after:=Worksheets(Worksheets.Count)
End If
ws.Range("A" & titlerow & ":A" & lr).EntireRow.Copy Sheets(myarr(i) & "").Range("A1")
Sheets(myarr(i) & "").Columns.AutoFit
Next
ws.AutoFilterMode = False
ws.Activate
End Sub
Now, what I'd really like is for excel to search through the dates in column C, and depending on month, move them into the relevant sheet, but in a way that if I update the main sheet, the monthly sheets are updated automatically. I don't know if this is possible, but surely it must be (it probably isn't even difficult). If necessary, I'd be happy to put in a further column with "Jan 15", "Feb 15" etc. or have a button that I can press to update everything.
Any help would be greatly appreciated!
Your code looks like a bit of an overkill, here I wrote a piece of code that would do the job if extended a bit, you need to add some cases, secure from errors in case the sheet is already there, and adjust the paste location but its a start(will also have more learning value for ya) :)
Sub haha()
Dim ws As Worksheet
Dim i As Integer
Dim lastrow
Set ws = ActiveSheet
lastrow = ws.Cells(ws.Rows.Count, "C").End(xlUp).Row
For i = 1 To lastrow
Select Case Format(ws.Range("c" & i).Value, "mm")
Case "01"
Sheets.Add.Name = "Jan"
ws.Range("C" & i).EntireRow.Copy Sheets("Jan").Range("A1")
End Select
Next i
End Sub
cheers
If it were going to be a code that you wanted to put to a button, I would do something like:
dim b2 as Workbook
Set b2=ThisWorkbook
xrowx=1
datecol='whatever column that you have the "Feb15" "Jan 15" data in
Do While xrowx<=Worksheetfunction.CountA(b2.Sheets(1).Range("A:A"))
month=Left(b2.Sheet(1).cells(xrowx,datecol))
if month="Jan" then
emptyrow=Worksheetfunction.CountA(b2.sheets(2).Range("A:A")+1
col=1
Do While col<=datecol
b2.sheets(2).cells(emptyrow,col)=b2.Sheets(1).Cells(emptyrow,col)
col=col+1
Loop
elseif month="Feb" then
emptyrow=Worksheetfunction.CountA(b2.sheets(3).Range("A:A")+1
col=1
Do While col<=datecol
b2.sheets(3).cells(emptyrow,col)=b2.Sheets(1).Cells(emptyrow,col)
col=col+1
Loop
elseif ...
...'continue on in this manner for all months
xrowx=xrowx+1
Loop
It's not super pretty or the most optimized way to get it done, but it is easy to understand and should give you a decent framework to build something that suites your needs well (Note: this code could also be used to auto-update as part of a built in sheet macro, but due to the fact that with very large data sets it will be a bit sluggish, that' not recommended)

Excel VBA: Compiler Errors

So yesterday I posted my first SO question, and it went down like a ton of bricks. However I've picked myself up, dusted myself off, and hopefully this question will be more acceptable... :-)
I am trying to remove data duplicates from a list of Health Questionnaires I have to monitor, but the tricky bit I was struggling with was finding a duplicate in one column, AND then checking that the data on the same row, for the 3 adjacent columns were also duplicates. Storing the searched for 'duplicated row' was the bit that was throwing me off.
Here's some code I've cobbled together from other similarly-functioning scripts. I'm now in debug mode and keep getting errors thrown up... I don't have much experience of VBA, so i'm running out of options.
I'm currently getting type mismatch errors with the variable g, and also firstAddress. Why are these causing problems???
Can I call firstAddress.Row or am I barking up the wrong tree?
Here's the snippet:
g = .Find(Range("G" & i).Text, LookIn:=xlValues)
If Not g Is Nothing Then
firstAddress = g.Address
dupRow = firstAddress.Row
And here's the whole code below. Any help would be much appreciated!
Sub FindCpy()
Dim lw As Long
Dim i As Integer
Dim sh As Worksheet
Dim dupRow As Integer
Dim g As Integer
Dim firstAddress As Integer
'Used for the new worksheet we are pasting into
Dim objNewSheet As Worksheet
Dim rngNextAvailbleRow As Range
'Used to narrow down the logical operators for duplicates
Dim rngFirst As Range
'Set the ranges
rngFirst = Range("G" & 1, "G" & lw)
Set sh = Sheets("Completed")
lw = Range("A" & Rows.Count).End(xlUp).Row
For i = 1 To lw 'Find duplicates from the list.
If Application.CountIf(Range("A" & i & ":A" & lw), Range("A" & i).Text) = "Complete" Then
'if COMPLETE, check the rest of the sheet for any 'in progress' duplicates...
With Worksheets("Still In Progress").rngFirst
g = .Find(Range("G" & i).Text, LookIn:=xlValues)
If Not g Is Nothing Then
firstAddress = g.Address
dupRow = firstAddress.Row
If Range("H" & dupRow).Text = Range("H" & i).Text _
And Range("I" & dupRow).Text = Range("I" & i).Text _
And Range("J" & dupRow).Text = Range("J" & i).Text Then
'select the entire row
Range.EntireRow.Select
'copy the selection
Selection.Cut
'Now identify and select the new sheet to paste into
Set objNewSheet = ThisWorkbook.Worksheets("Completed")
objNewSheet.Select
'Looking at your initial question, I believe you are trying to find the next available row
Set rngNextAvailbleRow = objNewSheet.Range("A1:A" & objNewSheet.Cells(Rows.Count, "A").End(xlUp).Row)
Range("A" & rngNextAvailbleRow.Rows.Count + 1).Select
ActiveSheet.Paste
'delete the initial row
rngCell.EntireRow.Delete
Set g = .FindNext(g)
Loop While Not g Is Nothing And g.Address <> firstAddress
End If
End With
Next i
End Sub
I went through your code carefully. There were a number of problems. Some of these I think I was able to fix - there was one where I guessed what you intended to do, but for one of them I just marked it; you need to explain what you were trying to do, as you are deleting a range that you never defined...
The first problem is with the line:
If Application.CountIf(Range("A" & i & ":A" & lw), Range("A" & i).Text) = "Complete" Then
The CountIf function returns a number; you are comparing this number with the string "Complete". I don't think you can ever get past this line, so the rest of the code (whether correct or not) will not execute. Not entirely clear what you are trying to do in this line, as I'm not sure when a line will be marked "Complete" - but assuming that you are interested in executing the rest of the code if the cell in A & i has the string "Complete" in it, then you probably want to do
If Range("A" & i).Text = "Complete" Then
There were a number of If - Then, With, and Loop structures that were not properly terminated with a matching End. I have tried to remedy this - make sure I did it right. Note that using proper indentation really helps to find problems like this. The space bar is your friend...
Since the Find method returns an object, the correct way to use the function is
Set g = .Find(Range("G" & i).Text, LookIn:=xlValues)
Apart from that - use Option Explicit at the top of your code, and define variables with the most restrictive (correct) type that you can. When I did this I found the error I could not correct - with the rngCell variable that was neither declared, nor ever set... It shows just how helpful it can be. Also good for catching typos - VBA will happily let you write things like
myVar = 1
MsgBox myVra + 1
The message will be 1, not 2, because of the typo... The fact that Explicit should even be an option is one of the many inexplicable design decisions made by the VBA team.
Here is your code "with most of the errors fixed". At least like this it will compile - but you must figure out what to do with the remaining error (and I can't be sure I guessed right about what you wanted to do with the cell marked "Complete").
Comments welcome.
Option Explicit
Sub FindCpy()
Dim lw As Long
Dim i As Integer
Dim sh As Worksheet
Dim dupRow As Integer
Dim g As Range
Dim firstAddress As Range
'Used for the new worksheet we are pasting into
Dim objNewSheet As Worksheet
Dim rngNextAvailbleRow As Range
'Used to narrow down the logical operators for duplicates
Dim rngFirst As Range
'Set the ranges
rngFirst = Range("G" & 1, "G" & lw)
Set sh = Sheets("Completed")
lw = Range("A" & Rows.Count).End(xlUp).Row
For i = 1 To lw 'Find duplicates from the list.
' If Application.CountIf(Range("A" & i & ":A" & lw), Range("A" & i).Text) = "Complete" Then
If Range("A" & i).Text = "Complete" Then
'if COMPLETE, check the rest of the sheet for any 'in progress' duplicates...
With Worksheets("Still In Progress").rngFirst
Set g = .Find(Range("G" & i).Text, LookIn:=xlValues)
If Not g Is Nothing Then
firstAddress = g.Address
dupRow = firstAddress.Row
If Range("H" & dupRow).Text = Range("H" & i).Text _
And Range("I" & dupRow).Text = Range("I" & i).Text _
And Range("J" & dupRow).Text = Range("J" & i).Text Then
'select the entire row
g.EntireRow.Select
'copy the selection
Selection.Cut
'Now identify and select the new sheet to paste into
Set objNewSheet = ThisWorkbook.Worksheets("Completed")
objNewSheet.Select
'Looking at your initial question, I believe you are trying to find the next available row
Set rngNextAvailbleRow = objNewSheet.Range("A1:A" & objNewSheet.Cells(Rows.Count, "A").End(xlUp).Row)
Range("A" & rngNextAvailbleRow.Rows.Count + 1).Select
ActiveSheet.Paste
'delete the initial row
rngCell.EntireRow.Delete ' <<<<<< the variable rngCell was never defined. Cannot guess what you wanted to do here!
Do
Set g = .FindNext(g)
Loop While Not g Is Nothing And g.Address <> firstAddress
End If ' entire row matched
End If ' Not g Is Nothing
End With ' With Worksheets("Still in Progress")
End If ' CountIf = "Complete"
Next i
End Sub
Another handy trick: when you "paste in the next available row" as you are doing with Range("A" & rngNextAvailbleRow.Rows.Count + 1).Select, I usually find it handy to do something like this instead:
Dim destination As Range
Set destination = Worksheets("Sheetname").Range("A1")
And when you need to paste something:
destination.Select
ActiveSheet.Paste
Set destination = destination.Offset(1,0)
This way, destination is always pointing to the "next place where I can paste". I find it helpful and cleaner.

Setting Range in For Loop

I am trying to set the range in For loop. My code works fine when I do this:
For Each i in Range("A1":"A5")
'Some process code
Next i
But I do not get the same results when I do this:
For Each i in Range("A1").End(xlDown)
'Some Process
Next i
Arent the two codes equivalent? What changes should I make to the second one that it perfoms the same way as the first one but doesn't make me hardcode the Range in the code?
The second one you have only gets the last cell in the range, which I believe would me A5 from the first example. Instead, you need to do something like this.
I structured this like a small test so you can see the first option, the corrected second, and an example of how I would prefer to do this.
Option Explicit
Sub test()
Dim r As Range
Dim x As Range
' Make sure there is stuff in Range("A1:A5")
Range("A1") = 1
Range("A2") = 2
Range("A3") = 3
Range("A4") = 4
Range("A5") = 5
' Your first option
For Each x In Range("A1:A5")
Debug.Print x.Address & ", " & x
Next
' What you need to do to get the full range
For Each x In Range("A1", Range("A1").End(xlDown))
Debug.Print x.Address & ", " & x
Next
' My preferred method
Set r = Range("A1").End(xlDown)
For Each x In Range("A1", r)
Debug.Print x.Address & ", " & x
Next
End Sub
The cleanest way to do it would probobly be to store the lastRow number in a variable like so. You can do the concatenation in the for each line:
Dim cell as range
Dim lastRow As Long
lastRow = Range("A" & Rows.Count).End(xlUp).row
For Each cell In Range("A1:A" & lastRow)
Please note that it makes a difference between using xlUp and xlDown.
xlUp gives you last cell used in column A (so you start at rows.count)
XlDown gives you last non-blank cell (you can use range("A1").End(xlDown).Row)
You'll notice a lot of people use "A65536" instead of rows.count, but 65536 is not the limit for some versions of Excel, so it's always better to use rows.count.