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

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!

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 rows from Target sheet to oter sheets based on cell values

I am having some difficulty with (vba lookup) issue.
I Have a sheet (sheet3) which has multiple rows of data of different invoices (each row of data includes the invoice number it relates to)Data sheet
I have copied the unique invoice numbers into separate sheets, each invoice has its own sheet and the invoice number is in cell B1.invoice sheet
What I want to do is to copy all rows from the data sheet to the sheet with the matching invoice number.
all I have for my current code is this which My separate invoice pages link of rather than using Vba to create them as there will be various other formatting and Formulrs on the page so im pretty much starting from scratch on my issue!
Private Sub CommandButton1_Click()
Dim s1 As Worksheet, s2 As Worksheet
Set s1 = Sheets("sheet3")
Set s2 = Sheets("Bill Date")
s1.Range("F:G").Copy s2.Range("A:B")
s2.Range("A:B").RemoveDuplicates Columns:=1, Header:=xlNo
End Sub
Your help will be appreciated
Thanks
In your VBA Macro, do this within a for loop:
Sub copyData()
Dim invNo As String
Dim lastRow As Integer
Dim sourceSht As Worksheet
Dim targSht As Worksheet
Set sourceSht = Worksheets("Sheet3")
'evaluates every data item from row 2 to last populated row
For Row = 2 To sourceSht.Cells(sourceSht.Rows.Count, 1).End(xlUp).Row
invNo = sourceSht.Range("F" & Row).Value
'if invNo blank, skip
If invNo <> "" Then
'try to find the sheet, make if does not exist
invNo = invNo & "_INV"
On Error Resume Next
Set targSht = Worksheets(invNo)
If targSht Is Nothing Then
Worksheets.Add(After:=Worksheets(Worksheets.Count)).Name = invNo
Set targSht = Worksheets(invNo)
'SetHeader
End If
'find first empty row in targSht
lastRow = targSht.Cells(targSht.Rows.Count, 1).End(xlUp).Row + 1
'copy row of data
sourceSht.Range("A" & Row & ":L" & Row).Copy
targSht.Range("A" & lastRow & ":L" & lastRow).Select
targSht.Paste
'must do to make more sheets
Set targSht = Nothing
End If
Next
End Sub
I changed some of your specifications in favor of a simpler approach. I assumed the twelve columns you showed me are all you have. I added "_INV" to the end of the invoice sheets because purely numeric sheet names can cause errors. I am also pasting the row of data into the new sheet verbatim. If you keep your current header, you will need to change the order. You may consider changing your targSht header to make it easier. SetHeader is a placeholder for a block of code that sets up the header row in targSht however you want. Please mark correct if this solves your issue.
Demo (without invoice header):

Select Method of Worksheet Class Failed

I have this sub in Excel 2010 which is supposed to filter through all the cells in a sheet until it finds a match to Proj No, then paste a field from this row into another field.
When I try to run the sub, it gives me an error 1004: Select Method of Worksheet Class Failed. I've marked the line where this occurs. Any assistance would be greatly appreciated.
Option Explicit
Private Sub btnNext_Click()
Dim ProjNo As String
Dim Col As String
Dim Row As String
Dim cell As Range
Unload Dialog
formWait.Show
Sheets("Sheet7").Activate
ProjNo = Worksheets("Sheet1").Range("D6").Value
Col = Cells(Rows.Count, "A").End(xlUp).Row
For Each cell In Range("A2:A" & Col) 
If cell.Value = ProjNo Then
Row = Row & cell.Row
End If
Next cell
Workbooks("Form.xlsm").Sheets("Sheet7").Range("Row, 6").Copy Destination:=Sheets("Sheet1").Range("19, 5") ‘Error
Unload formWait
End Sub
I don't know what GWP is, but I think you want to use ProjNo there. The Range property doesn't accept an argument like that. Unless you have a named range of "Row,6" which you don't because it's not a legal name, you have to supply Range with a valid range reference, like A6 or D2:D12, for example.
Also, you can't concatenate rows and use them in a Range reference to get a larger range. You would have to copy each row inside the loop, union the ranges as you go, or better yet, filter on the value that you want and copy the visible rows.
Try this:
Private Sub btnNext_Click()
With ThisWorkbook.Worksheets("Sheet7")
'filter for the project id
.Range("A1", .Cells(.Rows.Count, 1).End(xlUp)).Resize(, 6).AutoFilter 1, "=" & .Range("D6").Value
'copy the visible rows
.Range("F2", .Cells(.Rows.Count, 6).End(xlUp)).SpecialCells(xlCellTypeVisible).Copy _
ThisWorkbook.Worksheets("Sheet1").Cells(19, 5)
'get rid of the filter
.AutoFilterMode = False
End With
End Sub
There are a few confusing items in your code above, so I wanted to place them long-form here. Let's get started:
Dim Col As String
Dim Row As String
It looks like your design expects these to be of type Long rather than type String. Even if these variables were meant to be strings, I would recommend adjusting their names -- when your fellow developer attempts to review your design, he or she is likely to see names like "Col" or "Row" and think "these are numbers". Easy fix:
Dim Col As Long, Row As Long
The next issue comes up here:
Col = Cells(Rows.Count, "A").End(xlUp).Row
The structure above is a common method for identifying the last ROW, not column. (It also appears that you have switched the "A" and number, which is another easy fix). While it is perfectly acceptable syntactically to name the variable for last row "Col", human users are likely to find this confusing. Identifying the last row (and the last col, which you use in the For Each loop), as explained in fantastic detail here, would be better handled like this:
Dim SheetSeven As Worksheet, SheetOne As Worksheet
Dim LastRow As Long, LastCol As Long
Set SheetSeven = ThisWorkbook.Worksheets("Sheet7")
Set SheetOne = ThisWorkbook.Worksheets("Sheet1")
With SheetSeven
LastRow = .Range("A" & .Rows.Count).End(xlUp).Row
LastCol = .Range("A" & .Columns.Count).End(xlToLeft).Column
End With
This should make your For Each loop look like this:
With SheetSeven
For Each cell in .Range("A2:A" & LastCol)
'... do you comparison and row incrementing here
Next cell
End With
Once you've identified your sheet as a variable, the Range.Copy action should be much easier as well:
With SheetSeven
.Range(.Cells(Row, 6)).Copy _
Destination:=SheetOne.Range(SheetOne.Cells(19, 5))
End With
Also one other thing you may wish to check is the status of Application.ScreenUpdating.
With the release of Office 2013 and later, a SDI (Single Document Interface) was introduced. If Application.ScreenUpdating is False and the workbook is not active, the implied call to Workbook.Activate will fail. Check the status of ScreenUpdating and set it to True if needed. You can set it back to False after the first Activate call for that workbook is made.
See this article:
https://support.microsoft.com/en-us/help/3083825/excel-workbook-is-not-activated-when-you-run-a-macro-that-calls-the-wo
In my case the error came as the sheet was hidden.
so I check if I am not working with the hidden sheet. Or you need to unhide the sheet before you try to select or activate sheet.
For Each sh In ThisWorkbook.Sheets
If Left(sh.Name, 8) <> "Template" Then
sh.Select
sh.Range("A1").Select
End If
Next

Application defined or object defined error on code

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.

Excel macro to concatenate one row at a time to end of file

I need an Excel macro to join seven columns of data on each row until the end of the data is reached. For example if I have a formula like this:
=A1&B1&C1&D1&E1&F1&G1
How can I write the macro so that it increments for every row to the end of the file in a sequence like this?
=A1&B1&C1&D1&E1&F1&G1
=A2&B2&C2&D2&E2&F2&G2
=A3&B3&C3&D3&E3&F3&G3
With so many answers, the main focus on what assylias and I were highlighting has gone to waste :)
However, if you still want a VBA answer. Use this method. This is much faster than Looping or an Autofill.
Option Explicit
Sub Sample()
Dim LastRow As Long
Dim Ws As Worksheet
Set Ws = Sheets("Sheet1")
LastRow = Ws.Range("A" & Ws.Rows.Count).End(xlUp).Row
'~~> If your range doesn't have a header
Ws.Range("H1:H" & LastRow).Formula = "=A1&B1&C1&D1&E1&F1&G1"
'~~> If it does then
Ws.Range("H2:H" & LastRow).Formula = "=A2&B2&C2&D2&E2&F2&G2"
End Sub
If you have 1000's of rows then you might want to switch off Screenupdating and change Calculation to Manual before you run the code and then reset them at the end of the code.
I think the easiest way to do this would be to just fill down as assylias says but if you want to use VBA:
Selection.AutoFill Destination:=Range("Your Fill Range"), Type:=xlFillDefault
Should copy across the other rows.
I agree 100% with the comments and the other answers, why do you need VBA to do this, but just to answer your original question, this is how I would accomplish it:
Sub FillAllWithFormula()
Dim i As Variant
Dim wsht As Worksheet
'If you are using this for a specific Worksheet use the following
Set wsht = ThisWorkbook.Worksheets(yourWorksheetName)
'or if you are always using this for the active sheet use the following
Set wsht = ActiveSheet
For i = 1 To wsht.Rows.Count
'Replace "X" with the column letter you want your formula to appear in
wsht.Range("X" & i).Formula = "=A" & i & "&B" & i & "&C" & i & "&D" & i & "&E" & i & "&F" & i & "&G" & i
Next
Set wsht = Nothing
End Sub