I have a workbook which will consist of an inconsistent number of worksheets (so could be <10 or >100 at any given time).
The values column will always be in column G on each and every worksheet. I have written the following to achieve a total at the bottom of column G in each worksheet:-
Sub WorksheetLoop()
Dim WS_Count As Integer
Dim I As Integer
Dim LR As Long
WS_Count = ActiveWorkbook.Worksheets.Count
For I = 1 To WS_Count
LR = Range("G" & Rows.Count).End(xlUp).Row
Range("G" & LR + 1).Formula = "=SUM(G4:G" & LR & ")"
Worksheets(ActiveSheet.Index + 1).Select
Next I
End Sub
It runs, however once the final worksheet has been totalled, I get the following runtime error:-
Run-time error '9': Subscript out of range
I've been looking at other threads and applying different approaches, some also work but trigger a runtime error 91.
If I place a end or exit to the for statement it fails after processing just one calculation.
I believe the issue lies in Worksheets(ActiveSheet.Index + 1).Select and falling over when there is no activesheet to process.
Worksheets in a workbook are a Collection. The good thing about collections is that you can step through each item in the collection using a For...Each loop.
The code below will step through each Worksheet in the Worksheets collection in the workbook that contains the code (ThisWorkbook). You can change this to ActiveWorkbook which will do the same for whichever workbook happens to be active at the time.
Rather than return the row number of the last cell in column G the code returns a reference to the actual cell and then Offsets by one row to get the next blank row.
R1C1 style is used for range referencing - R4C means row 4 in whichever column the formula is. R[-1]C means the row above the formula in whichever column the formula is in. So R4C:R[-1]C placed in cell G8 would mean G4:G7.
Sub WorkSheetLoop()
Dim wrkSht As Worksheet
Dim rLastCell As Range
For Each wrkSht In ThisWorkbook.Worksheets
Set rLastCell = wrkSht.Cells(wrkSht.Rows.Count, 7).End(xlUp)
If rLastCell.Row > 4 Then
rLastCell.Offset(1).FormulaR1C1 = "=SUM(R4C:R[-1]C)"
End If
Next wrkSht
End Sub
Why your version doesn't work
The reason why you're getting runtime error #9 is because your code is trying to select a sheet that doesn't exist.
For example, your code starts with the first sheet selected in a two sheet workbook:
Loop 1:
LR is found for the first sheet.
The SUM formula is added to the first sheet.
The second sheet is selected.
Loop 2:
LR is found for the second sheet.
The SUM formula is added to the second sheet.
The code attempts to select the third sheet which doesn't exist - error occurs.
Your code would work if you removed the Worksheets(ActiveSheet.Index + 1).Select line and added Worksheets(I).Select as the first line in your loop.
Also, your code can start with any sheet selected - if the last sheet is active when the code starts then it will add the sum to that sheet and then try and select the next sheet causing it to fail on the first loop.
Once you get to the last worksheet in the worksheets collection, trying worksheets(ActiveSheet.Index + 1).Select is trying to select something that doesn't exist.
Since there is no actual reason to select the worksheet to add a formula, avoid using Select.
Dim I As Integer, LR As Long
For I = 1 To ActiveWorkbook.Worksheets.Count
with ActiveWorkbook.Worksheets(I)
LR = .Range("G" & .Rows.Count).End(xlUp).Row
.Range("G" & LR + 1).Formula = "=SUM(G4:G" & LR & ")"
end with
Next I
Related
I am aware that there are many questions like this one in this forum. Yet, none of them gives satisfying reply.
I need a macro that will copy values from 3 cells from various sheets (all in the same Excel file): E6 (actually it is a merged cell containing columns EFG), E(FG)5 and E21. Then pastes those values into new sheet into columns A, B and C. There are 2 problems that do not let me solve this issue with traditional copy cell value code or answers in other threads in this forum:
There are 3 cells merged.
The number of worksheets might differ for different period of times, and they might change their names as well.
This is the code that I have found for another similar problem:
Sub CopyToMaster()
ShtCount = ActiveWorkbook.Sheets.Count
For i = 2 To ShtCount
Worksheets(i).Activate
Range("E6").Select
Selection.Copy
Sheets("Master").Activate
'Required after first paste to shift active cell down one
Do While Not IsEmpty(ActiveCell)
ActiveCell.Offset(1, 0).Select
Loop
ActiveCell.Offset(0, -3).Select
Selection.PasteSpecial
Next i
End Sub
Source Data (This is source data, I where I marked with yellow 3 cells that values, I need to copy):
Needed result (Here is the expected outcome, where each from previous yellow marked cells should be pasted in respective column):
Thx for your help.
Please, test the next (working) code. It should be faster than yours, not using clipboard. You must know that the value of a merged range is kept in its top left cell. So, having ranges with a single row, it is enough to try extracting the value of the first marge cells cell:
Sub CopyToMasterWorking()
Dim ws As Worksheet, wsM As Worksheet, lastR As Long, i As Long
Set wsM = Worksheets("Master")
wsM.UsedRange.Resize(wsM.UsedRange.rows.count - 1).Offset(1).ClearContents 'clear everything, except headers
For Each ws In ActiveWorkbook.Worksheets
If ws.name <> wsM.name Then
lastR = wsM.UsedRange.SpecialCells(xlCellTypeLastCell).row + 1
wsM.Range("A" & lastR).Value = ws.Range("E6").Value
wsM.Range("B" & lastR).Value = ws.Range("E5").Value
wsM.Range("C" & lastR).Value = ws.Range("E21").Value
wsM.Range("D" & lastR).Value = ws.name 'you may comment this line if not necessary...
End If
Next ws
End Sub
I thought that it would be good to have a little traceability, I mean to know from which sheet the data comes (per row). If you do not need it, you may comment last code line from iteration between sheets.
The code also clear everything in "Master" sheet, except the header, before starting processing. If you need to add at the end of existing data, you have to comment that line, too.
Please, send some feedback after testing it. If something not clear enough, do not hesitate to ask for clarification...
I think your only problem is copying merged-cell ranges, correct? This shows how to copy a merged-cell range to 1) same-sized range 2) single cell 3) different-sized range:
Option Explicit
Sub sub1()
Dim variant1
Cells.Delete
' define a merged-cell range and populate:
Range("b2:c3").MergeCells = True
Range("b2:c3") = " B2:C3 "
' to copy to a like-sized merged-cell range:
Range("b5:c6").MergeCells = True
Range("b2:c3").Copy Range("b5:c6")
' to copy to a single cell
variant1 = Range("b2:c3").Value
Range("b8").Value = variant1
' to copy to a different-sized merged-cell range:
Range("b10:d12").MergeCells = True
variant1 = Range("b2:c3").Value
Range("b10:d12").Value = variant1
End Sub
After numerous failed attempts I am really hoping someone can with my problem. It theory what I am trying to do sounds easy enough but I have spent hours on it today with no success.
I have tried all the possible solutions from this thread but to no avail: Excel vba Autofill only empty cells
Also looked here : https://www.mrexcel.com/board/threads/macro-to-copy-cell-value-down-until-next-non-blank-cell.660608/
I am looking to autofill a formula down a column(a vlookup from another sheet) but if there is already populated cells then to skip and continue the formula in the next available blank cell. For example, in rows A2:A10, row A5 has a value in it, so the formula gets into in A2, then fills to A4, then skips A5, then continues in A6 to A10.
This below code works the first time you use it but then on the second run it debugs with a "Run-time error '1004' - No cells were found". I noticed it it putting the formula into the first cell (B2) and then debugging out.
Sub FillDownFormulaOnlyBlankCells()
Dim wb As Workbook
Dim ws1, ws2 As Worksheet
Dim rDest As Range
Set wb = ThisWorkbook
Set ws1 = Sheets("Copy From")
Set ws2 = Sheets("Copy To")
ws2.Range("A1").Formula = "=IFERROR(IF(VLOOKUP(A2,'Copy From'!A:B,2,FALSE)=0,"""",VLOOKUP(A2,'Copy From'!A:B,2,FALSE)),"""")"
Set rDest = Intersect(ActiveSheet.UsedRange, Range("B2:B300").Cells.SpecialCells(xlCellTypeBlanks))
ws2.Range("B2").Copy rDest
End Sub
Please, try the next code:
Sub FillDownFormulaOnlyBlankCells()
Dim wb As Workbook, ws1 As Worksheet, rngBlanc As Range
Set wb = ThisWorkbook
Set ws1 = wb.Sheets("Copy From")
On Error Resume Next
Set rngBlanc = ws1.Range("B2:B" & ws1.rows.count.End(xlUp).row).SpecialCells(xlCellTypeBlanks)
On Error GoTo 0
If Not rngBlanc Is Nothing Then
rngBlanc.Formula = "=IFERROR(IF(VLOOKUP(A2,'Copy From'!A:B,2,FALSE)=0,"""",VLOOKUP(A2,'Copy From'!A:B,2,FALSE)),"""")"
Else
MsgBox "No blanc rows exist in B:B column..."
End If
End Sub
After running it once and do not create any empty cell, of course there will not be any blanc cells, anymore, at a second run...
Thanks to FaneDuru for his suggestion but I actually came up with an alternative solution to my problem which I though I would post as it might help others with a similar issue.
On a separate sheet, I created 3 columns, first column is names I already have, 2nd column are the new names and the 3rd column is there to combine the first 2 columns together, then use this code to combine first 2 columns :
Sub MergeColumns()
Dim wb As Workbook
Dim ws1 As Worksheet
Dim LastRow As Long, i As Long
Set ws1 = Sheets("Your Sheet Name")
LastRow = ws1.Range("F" & Rows.Count).End(xlUp).Row
For i = 2 To LastRow
If ws1.Range("G" & i) <> "" Then
ws1.Range("I" & i) = ws1.Range("H" & i).Text & "" & ws1.Range("G" & i).Text
Else: ws1.Range("I" & i) = ws1.Range("H" & i)
End If
Next i
End Sub
Obviously changing the sheet name and columns letter to suit your requirements.
Essentially, I have data in three columns and a model on a separate tab. The data tab has 1,000 rows of data, and each entry will be run through the model, with results being pasted into the fourth column.
Here's what one iteration would look like, but I need it to loop through every row.
Worksheets("Data").Range("E2:G2").Copy _
Worksheets("Model").Range("B4:D4").PasteSpecial Paste:=xlPasteValues
Calculate
Worksheets("Model").Range("C120").Copy_
Worksheets("Data").Range("H2").PasteSpecial Paste:=xlPasteValues
Worksheets("Model").Range("C121").Copy_
Worksheets("Data").Range("I2").PasteSpecial Paste:=xlPasteValues
Worksheets("Model").Range("C122").Copy_
Worksheets("Data").Range("J2").PasteSpecial Paste:=xlPasteValues
Then we'd copy the next row of data from the Data tab (i.e., range E3:G3).
This seems like a classic loop scenario, but I don't know how to write it in VBA.
You can do this on a range, I see two ways you can do it, using a copy and paste or simply replicating a transposed version of the data:
'Copy and paste method
Worksheets("Model").Range("C120:C" & range("C" & rows.count).end(xlup).row).Copy 'Using the .end(xlup) will find the last row of data without looping until blank.
Worksheets("Data").Range("H2").PasteSpecial xlPasteValues,,,True 'The True here is what tells the pastespecial to transpose
'Transpose method
Worksheets("Data").Range("H2:J2").Value = application.transpose(Worksheets("Model").range("C120:C122"))
Each have their advantage, the Copy and Paste method is easier because you don't need to know the end column so it works easier for a dynamic range, the transpose method doesn't use the clipboard so is less impact on your system.
The better method code wise would be the transpose method.
You can then set up a simple For Next loop to run through as many data ranges as you want.
Dim DataRow As Long, MyDat As Worksheet, MyModel As Worksheet
Set MyDat = Worksheets("Data")
Set MyModel = Worksheet("Model")
For DataRow = 2 To MyDat.Range("E" & Rows.Count).End(xlUp).Row
MyModel.Range("B4:D4").Value = MyDat.Range("E" & DataRow & ":G" & DataRow).value
Calculate
MyDat.Range("H" & DataRow & ":J" & DataRow).Value = Application.Transpose(MyModel.Range("C120:C122"))
Next
This is a simple loop that finds the last row in "Data" and uses it for the loop defined in "Model".
The expected result of this is that the loop will begin at row 120 and continue until the last row in "Data", copying data from C120 through to C(lRow) and pasting it into the "Data" sheet.
Sub test()
' declare your variables so vba knows what it is working with
Dim lRow, i As Long
Dim wb As Workbook: Set wb = ThisWorkbook
Dim srcws As Worksheet: Set srcws = wb.Worksheets("Data")
Dim destws As Worksheet: Set destws = wb.Worksheets("Model")
' find the last row in Data
lRow = srcws.Cells(srcws.Rows.Count, 1).End(xlUp).Row
' iterate from 120 to the last row found above
For i = 120 To lRow
' copy /paste the data
srcws.cells(1, 3).Copy Destination:=destws.cells(2, 7 + i)
Next i
End Sub
Best way is to use the cells-function, where the first argument is the row and the second is the column. Since you want to inrement the source to copy from by one row at a time but increment the paste destination by one column by a time, this method will be suitable.
In addition, try to not use "copy-paste", focus on setting the value for a cell by referring to a the value attribute from the source to copy. Each time you copy and then paste into the destination, you will need an additional memory cell, resulting in a much longer elapsed time if you are working with a large range to copy.
The code below should do the job.
Sub CopyData()
Dim i As Integer
i = 8 ' Start pasting into column H
' Loop until a blank cell is found
Do While Not Selection.Value = 0
With Sheets("Data").Cells(i + 112, 3)
' Select each cell in "Data", starting on C120
.Select
' Copy the value into "Model", starting on H2
Sheets("Model").Cells(2, i).Value = .Value
End With
Loop
End Sub
Im working in a code in excel vba, to delete the rows if length value of a cell is not equal to 10
Im trying to avoid using filters bcause im using a file that contains like 1 millions of rows, and when using filters, the excel crash it.
this is what I need
For exemple
The Column A contain an ID numbers,
but if the length cell with the ID is not 10 characters
I want to delete the row, this row I doesn't need it
I searched around the forums and gathered some codes to create the following code
Sub DeleteRows()
Dim c As Range
Dim LR As Integer
Dim i As Integer
Dim sht As Worksheet
Set sht = Worksheets(2)
LR = sht.Cells(Rows.Count, "A").End(xlUp).Row
For i = 2 To LR
For Each c In sht.Range("A2:A" & LR).Cells
If Len(c.Value) <> 10 Then
c.EntireRow.Delete
End If '<---------here is the error
Next
Next
Range("A1").Select
End Sub
when the macro is running it get stuck, I have to press ESC to stop the macro and the error appears in the line End If
This macro delete the rows that are not meeting the condition of length when I press the ESC button
Is there a solution in this code?
or exist better metod to delete rows without using the filters?
Since you're deleting rows you should really be counting upward, since it will mess your count up. For example, i is on row 3 and then deletes row 3, now row 4 is in row 3, and i is going to continue on what used to be row 5. So instead work your way from the bottom up.
Sub DeleteRows()
Dim LR As Long
Dim i As Long
Dim sht As Worksheet
Set sht = Worksheets(2)
LR = sht.Cells(Rows.Count, "A").End(xlUp).Row
For i = LR to 2 Step -1
If Len(sht.cells(i,1).value)<>10 then
sht.Rows(i).delete
End If
Next
Range("A1").Select
End Sub
Re: Creating a master sheet from multiple sheets.
Multiple sheet description: table with many rows and columns. Columns headings are identical but rows vary. Each sheet is a date.
Task: to take a single value from a specific column (always happens to be column M). the value I want is the total of that column. Take this summed value and insert into a master sheet.
My attempt so far is:
Sub append_master_sheet()
Dim wAppend As Worksheet, wSheet As Worksheet
Dim LastRow As Long
Set wAppend = Worksheets("Master")
For Each wSheet In Worksheets
If wSheet.Name <> wAppend.Name Then
LastRow = WorksheetFunction.Max(3, wAppend.Cells(65536, 2).End(xlUp).Row)
wSheet.UsedRange.Resize(, 13).Copy Destination:=wAppend.Cells(LastRow, 2)
End If
Next wSheet
End Sub
1). it takes all 13 columns rather than only the 13th column. (I see that is because I have set it at 13 as I do not know how to cycle through the preceding columns and skip them to only return the 13th column data (and within this column return the total of the column, not the discrete line items
2) Besides returning all the data which is a problem, it actually consistently skips the final value in the column M.
Can you advise how to amend above code to
1) only return the summed value from column M in the multiple sheets (calendar dates) and insert into master.
thanks,
N
Is this what you are trying (UNTESTED)
Like I mentioned in the comment above, see THIS link on how to find a last row in a column.
I have commented the code so that you will not have a problem understanding it. But if you do, simply post back :)
Note: I am assuming that the last cell in Col M has the SUM
Option Explicit
Sub append_master_sheet()
Dim wAppend As Worksheet, wSheet As Worksheet
Dim wApLRow As Long, wShLRow As Long
Set wAppend = ThisWorkbook.Worksheets("Master")
'~~> Get the last row where the ouput should be placed
wApLRow = wAppend.Range("B" & wAppend.Rows.Count).End(xlUp).Row + 1
For Each wSheet In Worksheets
If wSheet.Name <> wAppend.Name Then
With wSheet
'~~> Fuind the last row in Col M which has the sum
wShLRow = .Range("M" & .Rows.Count).End(xlUp).Row
'~~> Copy over the values to Master Sheet
wAppend.Range("B" & wApLRow).Value = .Range("M" & wShLRow).Value
'~~> Increment the row for next output
wApLRow = wApLRow + 1
End With
End If
Next wSheet
End Sub