Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 years ago.
Improve this question
I'm still pretty new with VBA (learning for work, coming from a JS background) and I need a little help with this. My goal is: loop through each worksheet (except the "Summary" sheet, although I'm not sure how to exclude this from the loop) in the workbook and copy A2 in each sheet, as well as the last cell containing a value in column L of each sheet, and paste those next to each other in columns A and B on the "Summary Sheet", respectively. I'm not an expert with VBA syntax by any means, so if anyone has any way to refactor this (I know I don't need all the .select methods), I would appreciate it. Right now I'm getting an "invalid or unqualified reference" error on line 28. My goal is to learn, so if you have any input I would appreciate a short explanation of the logic. Thanks.
Sub Macro7()
'
' Macro7 Macro
'
' Keyboard Shortcut: Ctrl+c
Dim ws As Worksheet
Dim lastRow As Integer
Dim summaryRow As Integer
summaryRow = 1
For Each ws In ActiveWorkbook.Worksheets
'Copy item number and paste on Summary Page'
Range("A2").Select
Selection.Copy
Sheets("Summary").Select
Range("A" & summaryRow).Select
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
'Copy corresponding BOM item # and paste on Summary Page'
ws.Select
lastRow = .Cells(.Rows.Count, "L").End(xlUp).Row
Range("L" & lastRow).Select
Application.CutCopyMode = False
Selection.Copy
Sheets("Summary").Select
Range("B" & summaryRow).Select
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
summaryRow = summaryRow + 1
Next ws
End Sub
You can avoid using .Select¹ by constructing a With ... End With statement that passes the parent worksheet reference along to the cells and range references. Direct value transfer is more expedient and more efficient than copying and pasting. Additionally, it avoids involving the clipboard altogether.
Sub Macro7()
Dim ws As Worksheet
Dim lastRow As Long
Dim summaryRow As Long
summaryRow = 1
For Each ws In ActiveWorkbook.Worksheets
With ws
If LCase(.Name) <> "summary" Then
Worksheets("Summary").Range("A" & summaryRow).Resize(1, 2) = _
Array(.Range("A2").Value, .Cells(Rows.Count, "L").End(xlUp).Value)
summaryRow = summaryRow + 1
End If
End With
Next ws
End Sub
.¹ 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.
Your code looks fine apart from the '.' reference Jeeped pointed out.
I have just neatened up your code here as using .select is a little messy and long-winded (if you are building big macros it can add unnecessary work and slow it down a lot).
You can just do the commands straight from a range reference as shown below:
Sub Macro7()
Dim ws As Worksheet
Dim lastRow As Integer
Dim summaryRow As Integer
summaryRow = 1
For Each ws In ActiveWorkbook.Worksheets
'Copy item number and paste on Summary Page'
Range("A2").Copy
Sheets("Summary").Range("A" & summaryRow).PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
'Copy corresponding BOM item # and paste on Summary Page'
lastRow = ws.Cells(Rows.Count, "L").End(xlUp).Row
Application.CutCopyMode = False
ws.Range("L" & lastRow).copy
Sheets("Summary").Range("B" & summaryRow).PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
summaryRow = summaryRow + 1
Next ws
End Sub
I typically code out worksheets("").Range("").action everytime unless I am doing a lot of work on 1 sheet so that I can see easily what is going on when I glance over, but you'll find your own preference to working (don't forget you can step through your code with F8 to look at what it is doing every step of the way)
I hope this helps you start your VBA journey!
Related
Currently I have an excel sheet that works out certain prices which change everyday. I would like to have a vba button that records everyday's prices for reference, saving the last price of the data everyday (I update the price frequently during the day)
I wrote the code below but seem to be getting the error:
Compile error: Invalid Qualifier
[on the first LastRow.Offset() line]. I am quite new to vba and so any help would be appreciated
Private Sub CommandButton1_Click()
'Selecting the data to copy
Range("C23:O23").Select
Selection.Copy
'Find the last used row in Column B
Dim LastRow As Long
With ActiveSheet
LastRow = .Cells(.Rows.Count, "B").End(xlUp).Row
End With
'if still today, replace data, if not record the data on the next line
If LastRow = Date Then
LastRow.Offset(0, 1).Select
Selection.PasteSpecial Paste:=xlPasteValuesAndNumberFormats,
Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
Else
LastRow.Offset(1, 0) = Date
LastRow.offset(1, 1).Select
Selection.PasteSpecial Paste:=xlPasteValuesAndNumberFormats,
Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
End If
End Sub
UPDATE: So, Ive changed a bit of my code where I define the LastRow variable differnetly:
Dim LastRow As Range
Set LastRow = Range("B31").End(xlDown)
This seemed to lead to a different error, "1004", in the line just after the else statement
LastRow.Offset(1, 0).Value = "=today"
Any advice will be appreciated
LastRow is defined as Long
Dim LastRow As Long
And then you are trying to use an Offset method, which is only available on Range objects.
Make the following changes and you should be good to go.
Dim LastRow As Range
With ActiveSheet
Set LastRow = .Cells(.Rows.Count, "B").End(xlUp)
End With
Reading this post on how and why to avoid select will take you far too. The code above can be optimized to work much smarter.
Looks like you have a typo in your else statement:
LastRow.oofset(1, 1).Select
I have a workbook that serves as source data for another Excel-based form (not a Userform, just a formatted spreadsheet). The source file has anywhere from 2-40 rows of data - starting from row 18 - and each row needs to be copied into the form and saved separately, i.e. 15 rows from the Source file equates to 15 distinct Form files.
Each cell within the row must be copied separately and pasted to specific cells on the Form. The Source form contains Clients and their relevant info. I am trying to use a macro on the Form to automatically pull line items from the Source file, save the Form as the client's name in a specified folder, and continue until a blank row is reached on the Source file. I have some basic VBA experience, but have little knowledge of loops, variables, or functions, which seem to be my best course of action here.
Here's what I have so far. All I've been able to accomplish is the copy/pasting of the first row from the Source file.
Range("B18").Select
Selection.Copy
Windows("Form.xls").Activate
Range("F7:K7").Select
ActiveSheet.Paste
Windows("Source.xls").Activate
Range("C18").Select
Application.CutCopyMode = False
Selection.Copy
Windows("Processing Form.xls").Activate
Range("D8").Select
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
Windows("Source.xls").Activate
Range("D18").Select
Application.CutCopyMode = False
Selection.Copy
Windows("Processing Form.xls").Activate
Range("H29").Select
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
Windows("Source.xls").Activate
Range("E18").Select
Application.CutCopyMode = False
Selection.Copy
Windows("Processing Form.xls").Activate
Range("E29").Select
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
Windows("Source.xls").Activate
Range("F18").Select
Application.CutCopyMode = False
Selection.Copy
Windows("Processing Form.xls").Activate
Range("D33").Select
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
Range(“F7:K7”).Select
Application.CutCopyMode = False
Selection.Copy
ActiveWorkbook.SaveAs
I can't even get my macro to save the Form by the client's name. I also know that my extensive use of "Range" and "Select" will slow my code down to a crawl, I just don't know how to make it more efficient. I've tried using a reference cell to that tells the macro which row of the Source file to copy but haven't had any luck down that road either. Any and all help will be greatly appreciated!
Here's a simple demo:
Note: Not Tested
Option Explicit
Sub CopyToForm()
Dim wbSource As Workbook, wbForm As Workbook
Dim wsSource As Worksheet, wsForm As Worksheet
Dim formpath As String, foldertosavepath as string
Dim lrow As Long, i As Integer
Set wbSource = Thisworkbook '~~> assuming you write your code in Source.xls
Set wsSource = wbSource.Sheets("NameOfYourSheet") '~~> put the source sheet name
'~~> put the path where your form template is saved here
formpath = "C:\Users\Username\FolderName\Processing Form.xls"
'~~> put the path where you want to save individual updated forms.
foldertosavepath = "C:\Users\Username\FolderDestination\"
With wsSource
'~~> get the number of rows with data
lrow = .Range("B" & .Rows.Count).End(xlUp).Row
If lrow < 18 Then Msgbox "No data for transfer": Exit Sub
For i = 18 to lrow
Set wbForm = Workbooks.Open(formpath) '~~> open the form
Set wsForm = wbForm.Sheets("Sheetname") '~~> put the form sheet name
'~~> proceed with the copying
.Range("B" & i).Copy: wsForm.Range("F7:K7").PasteSpecial xlPasteValue
.Range("C" & i).Copy: wsForm.Range("D8").PasteSpecial xlPasteValues
.Range("D" & i).Copy: wsForm.Range("H29").PasteSpecial xlPasteValues
.Range("E" & i).Copy: wsForm.Range("E29").PasteSpecial xlPasteValues
.Range("F" & i).Copy: wsForm.Range("D33").PasteSpecial xlPasteValues
'~~> Save the form using the client name, I assumed it is in B?
wbForm.SaveAs foldertosavepath & .Range("B" & i).Value & ".xls"
wbForm.Close True
Set wbForm = Nothing
Set wsForm = Nothing
Next
End With
End Sub
In above code, I assumed that Form.xls is the same as Processing Form.xls.
This should give you the logic.
I hope this get's you started.
This is not test as I've noted, so if you encounter errors, comment it out.
You are frequently activating the workbooks. that's your code slows down..Below code will work faster
Sub test()
Dim dwb As Workbook
Dim swb As Workbook
Set dwb = Workbooks("Form.xls")
Set swb = Workbooks("Source.xls")
Set awb = Workbooks("Processing Form.xls")
With swb
.ActiveSheet.Range("B18").Copy Destination:=dwb.Sheet1.Range("F7:K7")
.ActiveSheet.Range("C18").Copy Destination:=awb.Sheet1.Range("D8")
.ActiveSheet.Range("D18").Copy Destination:=awb.Sheet1.Range("H29")
.ActiveSheet.Range("e18").Copy Destination:=awb.Sheet1.Range("E29")
.ActiveSheet.Range("F18").Copy Destination:=awb.Sheet1.Range("D33")
End With
End Sub
This might help steer you in the right direction:
Dim i As Long
For i = 1 To 10
With Range("A" & i)
.Copy Workbooks("ToWorkbook.xlsx").Worksheets("Sheet1").Range("B" & i + 9)
.Copy Workbooks("ToAnother.xlsx").Worksheets("Sheet2").Range("C" & i + 8)
.Copy Workbooks("AnotherOne.xlsx").Worksheets("SheetA").Range("D" & i + 2)
End With
Next i
i To 10 is used as a counter to loop through the rows in the source workbook.
For each i, you're taking the range from column A (i.e., with this, do something), copying and pasting it into different cells in different workbooks. In the first round, Range("A1") is being copied into 3 different workbooks at Range("B10"), Range("C9") and Range("D3"), respectively. The next turn, Range("A2") from the source book is going to be copied and pasted into the same destination workbooks from last time, but in Range("B11"), Range("C10") and Range("D4"). It's just a matter of finding the pattern for the different forms you need to paste into.
I've googled this but couldn't find a clear answer.
I have a workbook that contains lots of sheets, each sheet contains purchase order info.
I want to copy the same cell range from each sheet and compile a long list of all of those ranges.
my codes is currently;
Sub WorksheetLoop()
Sheets("5040001253").Select
Range("A4:O23").Select
Selection.Copy
Sheets("PO_Combi").Select
lastRow = ActiveSheet.Cells(Rows.Count, "A").End(xlUp).Row
ActiveSheet.Range("A" & lastRow + 1).Select
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
Application.CutCopyMode = False
End Sub
My question is: is there a simple command that allows to replace the sheet named 5040001253 with that will make this macro repeat on all sheets? If not, could someone tell me what to do to make it so?
Next code copies Range("A4:O23") from each sheet (except PO_Combi) to column A of sheet PO_Combi:
Sub WorksheetLoop()
Dim sh As Worksheet
Dim shCombi As Worksheet
Dim lastrow As Long
Set shCombi = ThisWorkbook.Worksheets("PO_Combi")
For Each sh In ThisWorkbook.Worksheets
With shCombi
If sh.Name <> .Name Then
lastrow = .Cells(.Rows.Count, "A").End(xlUp).Row
sh.Range("A4:O23").Copy
.Range("A" & lastrow + 1).PasteSpecial xlPasteValues
End If
End With
Next
Application.CutCopyMode = False
End Sub
I have 50 worksheets in a workbook. columns a,b,c,d are same as columns e,f,g,h, but both sets might have different number of rows/observations. I need to consolidate all in a single sheet having only 3 columns. I need to append the column names, start copying and pasting (values) from 3rd row onwards (till end of data). I tried recording a macro too but in that case, I have to go through all the sheets manually. Can someone lead me to the right direction? I'm very new to VBA and a little help will be much appreciated. My recorded macro for copying 2 sheets goes like this:
Sheets("page 9").Select
Range("A3:D3").Select
Range(Selection, Selection.End(xlDown)).Select
Selection.Copy
ActiveWindow.ScrollWorkbookTabs Position:=xlLast
Sheets.Add After:=Sheets(Sheets.Count)
Range("A2").Select
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
Selection.End(xlDown).Select
Range("A67").Select
ActiveWindow.ScrollWorkbookTabs Position:=xlFirst
Sheets("page 9").Select
Range("E3:H3").Select
Range(Selection, Selection.End(xlDown)).Select
Application.CutCopyMode = False
Selection.Copy
ActiveWindow.ScrollWorkbookTabs Position:=xlLast
Sheets("Sheet1").Select
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
Selection.End(xlDown).Select
Range("A132").Select
ActiveWindow.ScrollWorkbookTabs Position:=xlFirst
Sheets("page 10").Select
Range("A65").Select
Selection.End(xlUp).Select
Range("A3:D3").Select
Range(Selection, Selection.End(xlDown)).Select
Application.CutCopyMode = False
Selection.Copy
ActiveWindow.ScrollWorkbookTabs Position:=xlLast
Sheets("Sheet1").Select
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
Selection.End(xlDown).Select
Range("A197").Select
ActiveWindow.ScrollWorkbookTabs Position:=xlFirst
Sheets("page 10").Select
Range("E3:H3").Select
Range(Selection, Selection.End(xlDown)).Select
Application.CutCopyMode = False
Selection.Copy
ActiveWindow.ScrollWorkbookTabs Position:=xlLast
Sheets("Sheet1").Select
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
Selection.End(xlUp).Select
Range("A1").Select
Application.CutCopyMode = False
ActiveCell.FormulaR1C1 = "Date"
Range("B1").Select
ActiveCell.FormulaR1C1 = "Type"
Range("C1").Select
ActiveCell.FormulaR1C1 = "Size"
Range("D1").Select
ActiveCell.FormulaR1C1 = "Discount"
Range("A1").Select
End Sub
I doubt that anyone can decypher this code; certainly I lack the ability.
The Macro recorder is a great way of learning the syntax of new commands but it does not produce "good" code. It does not know your objective and records every little step as you do it.
Take the time to study Excel VBA. Search the internet for "Excel VBA Tutorial" or visit a good library or bookshop and select an Excel VBA Primer. There are many to chose from so I am sure you will find something that suits your learning style. This study will quickly repay your investment.
Look through the excel-vba questions on StackOverflow. Many, perhaps most, will be of no current interest to you. But some will show techniques you did not know about but which will be useful. Perhaps the most difficult aspect of learning VBA is discovering what is possible. Once you know statement X exists, you can look it up and study its syntax and functionality.
Below are four macros that demonstrate relevant code. Copy them to a workbook and try them. You could not have learnt how to write these macros from a study of macro recorder output.
A This macro outputs the name of every worksheet to the Immediate Window.
Sub A()
Dim InxWsht As Long
For InxWsht = 1 To Worksheets.Count
Debug.Print Worksheets(InxWsht).Name
Next
End Sub
B This adds a new worksheet at the end of the current list and names it "Consolidate". It then creates a bold, coloured header line.
Range(CellId).Value is one way of accessing a cell's value. I have used "A1" as the cells's Id but this is just a string and could have been built at runtime. Cells(RowId, ColId).Value is another way. RowId must be a number or an integer variable. ColId can be a number, an integer variable or a column letter. I suggest you be consistent and not mix and match as I have.
I show two method of specifying a range so I can set the entire header row bold and coloured in single statements.
If I have written Range("A1").Value = "Date" this statement would have operated on cell A1 of the active worksheet. The . before Range means this statement operates of cell A1 of the worksheet identified in the With statement. Using With means I do not have to switch worksheets using Select which is a slow command.
Sub B()
Dim WhshtCons As Worksheet
Set WhshtCons = Sheets.Add(After:=Sheets(Sheets.Count))
WhshtCons.Name = "Consolidate"
With WhshtCons
.Range("A1").Value = "Date"
.Cells(1, 2).Value = "Type"
.Cells(1, "C").Value = "Size"
.Cells(1, 4).Value = "Discount"
.Range("A1:D1").Font.Bold = True
.Range(.Cells(1, 1), .Cells(1, "D")).Font.Color = RGB(0, 128, 128)
End With
End Sub
C This outputs the value of Cell A1 of every worksheet except "Consolidate".
Sub C()
Dim InxWsht As Long
For InxWsht = 1 To Worksheets.Count
If Worksheets(InxWsht).Name <> "Consolidate" Then
With Worksheets(InxWsht)
Debug.Print "Cell A1 of Worksheet " & .Name & " contains [" & _
.Cells(1, 1).Value & "]"
End With
End If
Next
End Sub
D I will not explain this macro because it is somewhat more advanced than the others. It demonstrates moving columns of data from all the other worksheets to worksheet "Consolidate". I doubt this is close to what you seek but it demonstrates that what you seek is possible.
Sub D()
Dim ColConsCrnt As Long
Dim InxWsht As Long
Dim RowLast As Long
Dim WhshtCons As Worksheet
ColConsCrnt = 1
Set WhshtCons = Worksheets("Consolidate")
WhshtCons.Cells.EntireRow.Delete
For InxWsht = 1 To Worksheets.Count
If Worksheets(InxWsht).Name <> "Consolidate" Then
With Worksheets(InxWsht)
RowLast = .Cells(Rows.Count, "A").End(xlUp).Row
WhshtCons.Cells(1, ColConsCrnt).Value = .Name
.Range(.Cells(1, "A"), .Cells(RowLast, "A")).Copy _
Destination:=WhshtCons.Cells(2, ColConsCrnt)
End With
ColConsCrnt = ColConsCrnt + 1
End If
Next
End Sub
Welcome to programming. I hope you find it as much fun as I do.
I'm really new to programming in VBA and having a problem with this code I'm trying to write. I am wanting the code to figure out the first row in column A that is unused then copy and paste data from a different part of the sheet into that row.
Sub CopyandPaste()
Dim RowLast As Long
RowLast = ThisWorkbook.Worksheets("Sheet2").Cells(Rows.Count, "A").End(xlUp).Offset(1, 0).Row
Set NewRange = ThisWorkbook.Worksheets("Sheet2").Cells(RowLast, 1)
ThisWorkbook.Worksheets("Sheet1").Cells(8, "B").Select
Selection.Copy
Range("NewRange").Select
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
End Sub
Any help would be extremely helpful.
Try this code :
Sub CopyandPaste()
Dim RowLast As Long
ThisWorkbook.Activate
With Worksheets("Sheet2")
RowLast = .Cells(.Rows.Count, "A").End(xlUp).Offset(1, 0).Row
Sheets("Sheet1").Cells(8, "B").Copy Sheets("Sheet2").Cells(RowLast, 1)
End With
End Sub
I have added comments into the code explaining changes I made.
Sub CopyandPaste()
Dim RowLast As Long
Dim newRange As Range
'this works easier if I understand your intent right
'I generally use some large row number with Excel 2010
'You may ahve to make this smaller if you are in 03
RowLast = Sheets("Sheet2").Range("B99999").End(xlUp) + 1
'if you KNOW you have continuous data in this column (no spaces)
RowLast = Sheets("Sheet2").Range("B1").End(xldown) + 1
'this is slightly better way to do this
Set newRange = ThisWorkbook.Worksheets("Sheet2").Range("A" & RowLast)
'don't do this
'ThisWorkbook.Worksheets("Sheet1").Cells(8, "B").Select
'Selection.Copy
'do this instead
Sheets("Sheet1").Range("B8").Copy
newRange.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
'you were attempting to use a variable name (newrange) as a
'name of a named range in the Excel sheet
'use the variable range itself (as above)
End Sub