VBA Error 1004 While Setting Ranges Equal - vba

No matter what I do I can't get this error to go away. This current version of code is an effort to completely spell out each reference. I'm getting the error after the 'set range for BOM components' line. Most topics point out that using .Range(Cells()) without specifying where the cells are located will lead to this error, but I've done the complete overkill referencing so I'm not sure why it still errors out. Only one workbook here as well.
#Scott Craner #BruceWayne I figured it out finally; the Oracle report comes out in a pretty f****d up format, and the first sheet in the loop was causing the ranges to actually be unequal. As soon as I deleted that sheet it ran perfectly.
Sub Macro3()
'
' Macro3 Macro
'
' Keyboard Shortcut: Ctrl+c
'
Dim ws As Worksheet
Dim lastRow As Integer
Dim summaryRow As Integer
Dim currentSheetRows As Integer
Dim i As Integer
Dim j As Integer
summaryRow = 2
For Each ws In ActiveWorkbook.Worksheets
If ws.Range("L3") = "" Then
currentSheetRows = ws.Cells(ws.Rows.Count, "F").End(xlUp).Row
i = summaryRow
j = summaryRow
'set range for item # and item UOM'
For j = summaryRow To (summaryRow + (currentSheetRows - 3))
Sheets("Summary").Cells(j, 1).Value = ws.Cells(2, 1).Value
Next j
For i = summaryRow To (summaryRow + (currentSheetRows - 3))
Sheets("Summary").Cells(i, 2).Value = ws.Cells(2, 1).Value
Next i
'set range for BOM components'
ThisWorkbook.Worksheets("Summary").Range(ThisWorkbook.Worksheets("Summary").Cells(summaryRow, 3), ThisWorkbook.Worksheets("Summary").Cells((summaryRow + (currentSheetRows - 3)), 6)).Value = ThisWorkbook.Worksheets(ws.Name).Range(ThisWorkbook.Worksheets(ws.Name).Cells(3, 6), ThisWorkbook.Worksheets(ws.Name).Cells(currentSheetRows, 9)).Value
summaryRow = summaryRow + currentSheetRows
End If
Next ws
End Sub

Woah - first, check that the ranges are equal sizes. Then, I highly suggest using some variables for your sheet names and ranges:
Sub t()
Dim summaryWS As Worksheet
Dim otherWS As Worksheet
Set summaryWS = ThisWorkbook.Sheets("Summary")
Set otherWS = ThisWorkbook.Worksheets(ws.Name)
Dim copyRng As Range, pasteRng As Range
With summaryWS
Set copyRng = .Range(.Cells(SummaryRow, 3), .Cells((SummaryRow + (currentSheetRows - 3)), 6))
End With
With otherWS
Set pasteRng = .Range(.Cells(3, 6), .Cells(currentSheetRows, 9))
End With
pasteRng.Value = copyRng.Value
End Sub
Does that help your issue?

You're using
ActiveWorkbook
To set the loop current sheet
and then
ThisWorkbook
in the copy/paste values statement that errors
It may then be the workbook you're running the macro from (ThisWorkbook) differs from the currently active one (ActiveWorkbook) whose worksheets you're looping through
In such a case just change ThisWorkbook to ActiveWorkbook

The problem is here:
ThisWorkbook.Worksheets("Summary").Range(ThisWorkbook.Worksheets("Summary").Cells(summaryRow, 3), ThisWorkbook.Worksheets("Summary").Cells((summaryRow + (currentSheetRows - 3)), 6)).Value = ThisWorkbook.Worksheets(ws.Name).Range(ThisWorkbook.Worksheets(ws.Name).Cells(3, 6), ThisWorkbook.Worksheets(ws.Name).Cells(currentSheetRows, 9)).Value
the subscript is out of range.
Try
With Sheets(ws.name)
.Cells({var},{var}).Value = {var}

Related

Excel VBA run-time Error '424' Object Required

I've encountered a problem run-time Error '424' object required.
This is the solution i've acquired from my previous post, after spending some time to troubleshoot, i still can't solve it. Thanks to #rawrplus for the solution and I am still learning Excel vba.
Link: Excel VBA Cutting rows from workbook1 and paste it in workbook2
Option Explicit
Private Sub table_to_table()
'Declaration
Dim wb1 As Workbook
Dim wb2 As Workbook
Dim ws1 As Worksheet
Dim ws2 As Worksheet
Set wb1 = Workbooks.Open("C:\Documents and Settings\lye.yan.nian\My Documents\testingmacro2.xlsx")
Set wb2 = Workbooks.Open("C:\Documents and Settings\lye.yan.nian\My Documents\testingmacro3.xlsx")
Set ws1 = wb1.Sheets("Test2")
Set ws2 = wb2.Sheets("Test1")
Dim res_lr As Long
Dim lr2 As Long
lr2 = ws2.Cells(Rows.Count, 2).End(xlUp).Row
MsgBox lr2 /* Testing is done as i can get this value */
Dim r As Range, ary
Set r = ws1.Application.Range(Cells(1, 2), Cells(1, 6)) /* Tested working as i can get this value too*/
ary = Application.Transpose(Application.Transpose(r.Value))
MsgBox Join(ary, " ")
Dim copyrange As Range
Dim i As Long /* This is declared */
For i = 2 To lr2
MsgBox i /* i did some testing on this and removed the below code */
Set copyrange = ws2.Application.Range(Cells(i, 2), Cells(i, 6)).Copy /* This is the part where i got the error i can't figure out why*/
res_lr = ws2.Cells(Rows.Count, 8).End(xlUp).Row
ws2.Range(Cells(res_lr, 8), Cells(res_lr, 12)).PasteSpecial xlPasteValues
Set copyrange = ws1.Application.Range(Cells(i, 2), Cells(i, 6)).Copy
ws2.Range(Cells(res_lr + 1, 8), Cells(res_lr + 1, 12)).PasteSpecial xlPasteValues
Next i
wb1.Close
End Sub
Set copyrange = ws2.Application.Range(Cells(i, 2), Cells(i, 6)).Copy
Range.Copy does not return a Range object reference that you can assign to a Range object variable. It's a member method that takes the contents of a range, and copies it into the clipboard.
Your copyrange isn't the result of the .Copy operation, it's the result of a ws2.Application.Range(...) call... which is malformed.
This might fix it1:
Set copyrange = ws2.Application.Range(Cells(i, 2), Cells(i, 6))
copyrange.Copy
....and this is what you probably meant to be doing:
Set copyrange = ws2.Range(ws2.Cells(i, 2), ws2.Cells(i, 6))
copyrange.Copy
1 it's bad code, because you have an explicit Worksheet reference (ws2), and from there you go to Application to grab whatever worksheet is currently active, and work off that - so you think you're working off ws2 when in reality you're working off whatever ActiveSheet currently happens to be. Same with the unqualified Cells calls, which implicitly refer to Application.ActiveSheet.Cells.
Try adding the word set on the line where it throws error.
Example: set x = 'something

Merge Multiple Tabs in Excel Using Fixed Ranges

Apologies in advance as I'm sure this is a simple question, and there's lots of similar answers out there, but I haven't been able to leverage them into a working solution.
My situation is I have an Excel file with 28 tabs. Each Sheet has data in the exact same format in range A1:N10000. Note though that some of the cells in each tab are blank. This holds true across every tab. I would like to have all 28 tabs merged into one new Sheet call Combined.
I have been trying to leverage this Macro:
Sub Combine()
Dim J As Integer
On Error Resume Next
Sheets(1).Select
Worksheets.Add
Sheets(1).Name = "Combined"
Sheets(2).Activate
Range("A1").EntireRow.Select
Selection.Copy Destination:=Sheets(1).Range("A1")
For J = 2 To Sheets.Count
Sheets(J).Activate
Range("A1").Select
Selection.CurrentRegion.Select
Selection.Offset(1, 0).Resize(Selection.Rows.Count - 1).Select
Selection.Copy Destination:=Sheets(1).Range("A65536").End(xlUp)(2)
Next
End Sub
Obviously I've encountered problems running this and the resulting data only has a few values pasted instead of the expected ~280,000 rows (28 tabs, 10k rows each). I suspect one of the reasons is because there are blank cells in the tabs, so this Macro isn't reading the data as I intend it to. How can I modify it just to copy A1:N10000 in each tab and paste each to the Combined tab? Also, will I hit issues with trying to populate a sheet with 280,000 rows?
Thank you!
David
CurrentRegion will not copy everything you want if there are blank cells, as you suspected. Also, it's preferable to avoid using Select - since you don't really need to select the cells before copying - and On Error Resume Next - this doesn't handle errors at all, it ignores them.
Option Explicit
Sub Combine()
Dim i As Integer
Dim combinedWs As Worksheet, ws As Worksheet
Dim copyRng As Range
Dim lastRow As Long
' Add combined worksheet and populate headers
Set combinedWs = Worksheets.Add(Before:=Sheets(1))
combinedWs.Name = "Combined"
Sheets(2).Rows(1).Copy combinedWs.Rows(1)
' Loop through rest of Sheets
For i = 2 To Sheets.Count
Set ws = Sheets(i)
With ws
lastRow = .Cells(.Rows.Count, 1).End(xlUp).Row
Set copyRng = Range(.Cells(2, 1), .Cells(lastRow, "N"))
copyRng.Copy combinedWs.Cells(.Rows.Count, 1).End(xlUp).Offset(1)
End With
Next i
End Sub
If you want to copy a specific, hard-coded range, replace the code inside With ws... End With.
Set copyRng = Range(.Cells(2, 1), .Cells(10000, 14))
copyRng.Copy combinedWs.Cells(2, 1).Offset((i-2)*copyRng.Rows.Count)
A bit of a hack on the last line: for each iteration of i you are offsetting by the number of rows in copyRng. You start in combinedWs.Cells(2, 1), or A2. On the first iteration, i - 2 = 0, so there is no offset. On subsequent iterations, you offset 9999, 19998, and so on.
You can try the below code:
Sub Combine()
Dim cmbSheet, ws As Worksheet
Dim tmpIndex As Double
Set cmbSheet = ThisWorkbook.Worksheets.Add
cmbSheet.Name = "Combined"
tmpIndex = 0
For Each ws In ThisWorkbook.Worksheets
If ws.Name <> "Combined" Then
If tmpIndex = 0 Then
cmbSheet.Cells(1, 1) = "Sheet Name"
ws.Range("A1:N1").Copy Destination:=cmbSheet.Cells(1, 2)
End If
ws.Range("A2:N10000").Copy Destination:=cmbSheet.Cells((tmpIndex * 10000) + 2 - tmpIndex, 2)
cmbSheet.Cells((tmpIndex * 10000) + 2, 1).Value = ws.Name
tmpIndex = tmpIndex + 1
End If
Next ws
End Sub

Transferring only visible rows without using .select or .End(xlDown)

The code currently filters the data the way I need it to but when it copies it over it copies everything not just the filtered data. I know I can make it work using .select and then copying and pasting etc. but I would rather avoid that if possible. Any help would be appreciated.
Sub Auto_Filter()
Dim RNG As Range
Dim Open_Jobs_Report As Worksheet
Set Open_Jobs_Report = ThisWorkbook.Sheets("Open Jobs Report")
Dim Dashboard As Worksheet
Set Dashboard = ThisWorkbook.Sheets("Dashboard")
Dim PersonResponsible As Range
Dim Violations As Range
Dim CLM1 As Long
Dim CLM2 As Long
With Sheets("Open Jobs Report")
Set RNG = .Range("A1", .Cells(.Cells(.Rows.Count, "A").End(xlUp).Row, .Cells(1, .Columns.Count).End(xlToLeft).Column))
RNG.AutoFilter Field:=19, Criteria1:="<>"
CLM1 = .Range("1:1").Find(What:="Person Responsible").Column
Set PersonResponsible = .Range(.Cells(1, CLM1), .Cells(1, CLM1).End(xlDown))
CLM2 = .Range("1:1").Find(What:="Violations").Column
Set Violations = .Range(.Cells(1, CLM2), .Cells(1, CLM2).End(xlDown))
End With
Dashboard.Range("B4:B1000").Value = PersonResponsible.Value
Dashboard.Range("E4:E1000").Value = Violations.Value
Dashboard.Range("B4:B1000").RemoveDuplicates , Header:=xlYes
Dashboard.Range("E4:E1000").RemoveDuplicates , Header:=xlYes
Open_Jobs_Report.ListObjects(1).AutoFilter.ShowAllData
End Sub
This will only copy the visible rows from your table on the Open_Jobs_Report sheet
Open_Jobs_Report.ListObjects(1).DataBodyRange.SpecialCells(xlCellTypeVisible).Copy
Update after comments:
With Open_Jobs_Report.ListObjects(1)
Union(.ListColumns(9).DataBodyRange, .ListColumns(19).DataBodyRange).Copy
End With
Or using header names:
With Open_Jobs_Report.ListObjects(1)
Union(.ListColumns("Person Responsible").DataBodyRange, .ListColumns("Violations").DataBodyRange).Copy
End With
Thinking a bit more about it this is how I'd probably go about doing it:
Dim Open_Jobs_Report As Worksheet
Dim temp as Variant
Set Open_Jobs_Report = ThisWorkbook.Sheets("Open Jobs Report")
temp = Open_Jobs_Report.ListObjects(1).DataBodyRange.SpecialCells(xlCellTypeVisible).Value
With Dashboard
.Range(.Cells(4, 2), .Cells(3 + UBound(temp,1), 2)).Value = Application.Index(temp, , 9)
.Range(.cells(4, 5), .Cells(3 + UBound(temp, 1), 5)).Value = Application.Index(temp, , 19)
End With
This puts the entire visible part of the table into an array in memory and then sets the range equal to the bits you're interested in.
You are probably looking to use the the built-in AutoFilter.Range object of the worksheet.
ActiveSheet.AutoFilter.Range.Copy
Workbooks.Add.Worksheets(1).Paste
This link gives the full explanation

Invalid Qualifier error with Slope function

New to VBA and got this qualifier error for a code that's supposed to calculate the slope of two arrays and place it in a column with the given address.
Beats me so far, so it'd be nice to have some help! The sizes of the arrays are matching.
The compile error is given at the .Slope within the TargetSheet.Cells(n, (MyRange.Columns.Count) + 1).Value = Application.WorksheetFunction.Slope(TargetSheet.Range(TargetSheet.Cells(n, 5), TargetSheet.Cells(n, MyRange.Columns.Count)), TargetSheet.Range(TargetSheet.Cells(1, 5), TargetSheet.Cells(1, MyRange.Columns.Count))).Value
Dim n As Long
Dim MyRange As Range
Set MyRange = ActiveSheet.UsedRange
Dim TargetSheet As Worksheet, SourceSheet As Worksheet
Dim TargetBook As Workbook
Set TargetBook = Application.ActiveWorkbook
Set TargetSheet = Application.ActiveSheet
For n = 3 To MyRange.Rows.Count
TargetSheet.Cells(n, (MyRange.Columns.Count) + 1).Value = Application.WorksheetFunction.Slope(TargetSheet.Range(TargetSheet.Cells(n, 5), TargetSheet.Cells(n, MyRange.Columns.Count)), TargetSheet.Range(TargetSheet.Cells(1, 5), TargetSheet.Cells(1, MyRange.Columns.Count))).Value
Next n
End With
End Sub
A version before this one (can't undo till that point :/) was working up till a point where it stopped and gave the qualifier error. I used Excel's SLOPE function on the data set and compared alongside: it was giving this error at a #DIV/0 result (but the VBA didn't show that).
As stated, removing .Value from the slope call allows it to pass the compiler.
Otherwise, it seems to work.
This input ...
Provided this output ...
Using this code (slightly modified to get the correct columns) ...
Sub main()
Dim n As Long
Dim MyRange As Range
Set MyRange = ActiveSheet.UsedRange
Dim TargetSheet As Worksheet, SourceSheet As Worksheet
Dim TargetBook As Workbook
Set TargetBook = Application.ActiveWorkbook
Set TargetSheet = Application.ActiveSheet
For n = 2 To MyRange.Rows.Count
TargetSheet.Cells(n, (MyRange.Columns.Count) + 1).Value = _
Application.WorksheetFunction.Slope(TargetSheet.Range(TargetSheet.Cells(n, 1), TargetSheet.Cells(n, MyRange.Columns.Count)), _
TargetSheet.Range(TargetSheet.Cells(1, 1), TargetSheet.Cells(1, MyRange.Columns.Count)))
Next n
End Sub
Below is revised code to capture case when only 1 Y value is present to avoid divide by zero. This assumes that row 1 in the worksheet will have nothing to the right of the headers.
Option Explicit
Sub main()
Dim n As Long
Dim MyRange As Range
Dim nRows As Long, nCols As Long, ColCount As Long
Set MyRange = ActiveSheet.UsedRange
Dim TargetSheet As Worksheet, SourceSheet As Worksheet
Dim TargetBook As Workbook
Set TargetBook = Application.ActiveWorkbook
Set TargetSheet = Application.ActiveSheet
nRows = MyRange.Rows.Count
nCols = MyRange.Rows(1).End(xlToRight)
For n = 2 To nRows
ColCount = Application.CountIf(TargetSheet.Range(TargetSheet.Cells(n, 1), Sheet1.Cells(n, nCols)), """<>""""""")
If ColCount > 1 Then
TargetSheet.Cells(n, (MyRange.Columns.Count) + 1).Value = _
Application.Slope(TargetSheet.Range(TargetSheet.Cells(n, 1), TargetSheet.Cells(n, MyRange.Columns.Count)), _
TargetSheet.Range(TargetSheet.Cells(1, 1), TargetSheet.Cells(1, MyRange.Columns.Count)))
End If
Next n
End Sub
Thanks #OldUgly! Managed to fix the error. Reason for VBA: automate for multiple data sheets in the same format with varying no. of columns and rows.
WorksheetFunction.Slope doesn't like it when there's only one data point and the rest of one array is empty.
I created another loop to count non-empty cells within the For loop for each row. If the no. of non-empty cells is < 2 for a particular row, the output would be "Insufficient Data".
If not, then the slope value would be calculated.
Another, less painstaking way to get around this issue is to simply use On Error Resume Next for this specific case.
Dim n As Long, o As Range, CurrentRow As Range, NonEmptyCellCountRow As Integer
For n = 3 To MyRange.Rows.Count `Within each row, counting non-empty cells
Set CurrentRow = TargetSheet.Range(TargetSheet.Cells(n, 5), TargetSheet.Cells(n, MyRange.Columns.Count))
NonEmptyCellCountRow = 0
For Each o In CurrentRow
If o.Value <> "" Then NonEmptyCellCountRow = NonEmptyCellCountRow + 1
Next o
If NonEmptyCellCountRow < 2 Then _
TargetSheet.Cells(n, (MyRange.Columns.Count) + 1) = "Insufficient Data"
Else
TargetSheet.Cells(n, (MyRange.Columns.Count) + 1) = Application.WorksheetFunction.Slope(TargetSheet.Range(TargetSheet.Cells(n, 5), TargetSheet.Cells(n, MyRange.Columns.Count)), TargetSheet.Range(TargetSheet.Cells(1, 5), TargetSheet.Cells(1, MyRange.Columns.Count)))
End If
Next n

Clear entire row if no content/text/number found in cell

I am writing a VBA script that will delete (clear content) of the entire row if one of the cells within the rows is found to be blank or does not have any text or integer value.
I'm almost there, but I think my code is stuck in the for loop. Please let me know how I can further improve my code.
Sub Remove_Rows_BlankData()
For SheetCount = 1 To Sheets.Count 'SHEET LEVEL
Sheets(SheetCount).Visible = True
Sheets(SheetCount).Select
StartRow = 2
' EndRow = Cells(ActiveSheet.UsedRange.Rows.Count, 34)
LastRow = ActiveSheet.UsedRange.Rows.Count
LastCol = ActiveSheet.UsedRange.Columns.Count
Dim myRange As Range
Set myRange = Range(Cells(StartRow, 1), Cells(LastRow, LastCol))
'REMOVE ROWS W/ ANY BLANK CELLS
Dim DRow As Variant ' Sets DRow = Row w/ Blank
'From start row to last row
Cells.SpecialCells(xlCellTypeBlanks).EntireRow.Select
Selection.ClearContents
Next
End Sub
I've rewritten your sub to avoid having to Select a worksheet in order to use it. By referencing each worksheet in the loop using a With ... End With statement, the cells and properties of each worksheet can be dealt with without resorting to selecting¹ the worksheet just to use the inherent association of the ActiveSheet.
Sub Remove_Rows_BlankData()
Dim ws As Long, fr As Long, lr As Long, lc As Long
On Error Resume Next 'just in case there are no blank cells
For ws = 5 To Worksheets.Count 'SHEET LEVEL
With Worksheets(ws)
.Visible = True
'Sheets(SheetCount).Select 'not necessary to select in order to process
fr = 2
' EndRow = Cells(ActiveSheet.UsedRange.Rows.Count, 34)
lr = .UsedRange.Rows.Count
lc = .UsedRange.Columns.Count
With .Range(.Cells(fr, 1), .Cells(lr, lc))
.SpecialCells(xlCellTypeBlanks).EntireRow.ClearContents
End With
End With
Next ws
End Sub
Note that no variables are created (aka Dim) within the loop; only reassigned values.
¹ See How to avoid using Select in Excel VBA macros for more methods on getting away from relying on select and activate to accomplish your goals.