I am a newbie to VBA but got tasked to work with it anyway. So my task is to build a macro that takes data from different sheets and puts it below each other in one result sheet ("Tabelle1" in my example). The input data in each sheet is stored in blocks of two columns, right next to each other - so columns A and B have to import into the result sheet, then C and D and so on. Doing this for one sheet is not a problem:
Sub Makro1()
'
' Makro1 Makro
'
Dim Erste As Long
Dim k As Long
Dim j As Long
k = 1
j = 2
Do
Sheets("Tabelle1").Select
Erste = ActiveSheet.Cells(Rows.Count, 3).End(xlUp).Offset(1, 0).Row
Sheets("Tabelle2").Select
Range(Cells(5, k), Cells(5, j)).Select
Range(Selection, Selection.End(xlDown)).Select
Selection.Copy
Sheets("Tabelle1").Select
Cells(Erste, 3).Select
ActiveSheet.Paste
k = k + 2
j = j + 2
Loop Until Sheets("Tabelle2").Cells(4, k).Value = ""
End Sub
But I not only have one input sheet ("Tabelle2" in this example) but several (up until sheet20 or so). And all of them are built the exact same way, only with different data in each. What I would need the macro to do is, when reaching the empty cell in the first input sheet ("Tabelle2), go to the next input sheet ("Tabelle3") and continue the import of the data.
It doesn't sound too hard to do at first, but I cannot seem to find a solution. If anyone could help me out, it would be very much appreciated :-)
I know that the macro itself is very badly written and I can get rid of most of the Select. But as long as it works I'm fine.
Instead of using the name of the sheet, use the Index
Example: Sheet(1) and Sheet(2) etc
You can use that number as a variable that you can increment.
Example:
Dim i as Integer
Sheet(i).Select
Note:
It is also better practice to change the code to not rely on .Select as it can cause confusion and problems.
In addition, it would be better to use Worksheet(1) as charts can also be referred to as sheets.
Related
I have a text based system with people's names, and I need to copy and paste the first 4 cells (not a problem) and the last 5 cells to a different worksheet for analysis.
The problem arises with the transfer from the text based system the data is presented in, to the spreadsheet when it comes to people with spaces in their surname (ie, Da Silva). I use text to columns, which will give me a variant number columns, depending on the number of spaces in the name which is an issue.
I already have a crude solution, but the time it takes and the jumping about between screens while the macro is running looks very unprofessional. I don't get much spare time at work, and I don't have the tools to test this properly at home.
Can anyone help me get this to loop until the last empty cell, and basically neaten it up a little?
The code, which repeats 300 times, is as following:
Sheets("Late list Sorting").Select
Range("A2").End(xlToRight).Select
Range(ActiveCell.Offset(0, 0), ActiveCell.Offset(0, -5)).Select
Selection.Copy
Sheets("Late list").Select
Range("D4").Select
ActiveSheet.Paste
(...Repeat until...)
Range("A300").End(xlToRight).Select
Range(ActiveCell.Offset(0, 0), ActiveCell.Offset(0, -5)).Select
Selection.Copy
Sheets("Late list").Select
Range("D302").Select
ActiveSheet.Paste
Sheets("Late list Sorting").Select
If you only want the values (not formulae or formatting) then you can simply use Value=Value...
Dim i As Long, sWs As Excel.Worksheet, tWs As Excel.Worksheet
Set sWs = ThisWorkbook.Worksheets("Late list Sorting")
Set tWs = ThisWorkbook.Worksheets("Late list")
For i = 2 To 300
tWs.Range(tWs.Cells(i + 2, 4), tWs.Cells(i + 2, 8)).Value = sWs.Range(sWs.Cells(i, 1).End(xlToRight), sWs.Cells(i, 1).End(xlToRight).Offset(0, -5)).Value
Next
it is inefficient to use .Select, here's a great resource: How to avoid using Select in Excel VBA
To eliminate the screen flashes, put this at the beginning of your code. Remember to turn it back to true at the end.
Application.ScreenUpdating = False
You want to use a loop, where the variable i becomes the row number you will reference like so:
Range("A" & i).Value...
There are many ways to loop, here is one example:
For i = 1 to 300
//All your code here
Next i
I'm trying to build a formula:
=BDS(Bonds!J2& " ISIN","ISSUE_UNDERWRITER","Headers","Y")
In one sheet that takes a unique identifier from another table.
These formula builds me a table. After it builds me the table, I need to take the next row in the other sheet:
=BDS(Bonds!J3& " ISIN","ISSUE_UNDERWRITER","Headers","Y")
Then insert that formula a the end of the previous table built by the previous formula.
What I tried was getting the last row and then offsetting it by one, but I'm trying to figure out how to loop through it.
This is what i have tried:
Sub Formula2()Formula2 Macro
Range("A1").Select
ActiveCell.FormulaR1C1 = _
"=BDS(Bonds!R[1]C[9]& "" ISIN"",""ISSUE_UNDERWRITER"",""Headers"",""Y"")"
lRow = Cells(Rows.Count, 1).End(xlUp).Select
Selection.Offset(1, 0).Select
ActiveCell.FormulaR1C1 = _
"=BDS(Bonds!R[-53]C10& "" ISIN"",""ISSUE_UNDERWRITER"",""Headers"",""Y"")"
Range("A57").Select
End Sub
Image of Table, Im trying to iterate through the ISIN Column. It is column "J"
Although selection and .select are used by the macro recorder, they cause big problems when developing code. It's worth your time to learn how to replace them with range objects. So, while I'm not directly answering your question, I'm trying to give you the tools to do so.
I've shown an example below to illustrate (although I do not work with the BDS() function so I'm undoubtedly getting the details wrong). The main point is that if you learn to move around using the range object you'll be much better off.
Sub formula()
Dim r As Range, sh As Worksheet, bondR As Range, bondSh as Worksheet
set sh = ActiveSheet
set r = sh.range("A1")
Set bondSh = Worksheets("Bonds")
Set bondR = bondSh.Range("J1")
For i = 1 To 10
r.formula = "=BDS(bondR.offset(i,0) & "" ISIN"",""ISSUE_UNDERWRITER"",""Headers"",""Y"")"
Set r = r.Offset(i, 0)
Next i
End Sub
Here I'm defining one range object, r, to track the location on the active sheet, and another, bondR, for the location on the "Bonds" sheet. Once the initial locations of these ranges are defined, you can manipulate them using the .offset(row,col) function as I've done with the simple for-loop, moving down 1 row (but 0 columns) in each loop.
Feel free to ask questions.
I would like to create a excel Macro that allows to me copy an x number of rows of certain column. For Example, I have column K that I have in 10.500 rows. I want to copy 1000 lines each time and also the 500 lines at the end. any help with the coding part ? I looked on so many sites and no success. I don't need to paste the copied number in any other excel sheet. I just need the macro command to copy me 1000 lines every time from the column that I selected.
thank youuuuu very much and much appreciated !
Cheers
Select a column and run the sub procedure from the Macros dialog (Alt+F8). The first 1000 cells will be copied to the clipboard.
Paste the data into another program.
Return to Excel and run the sub procedure again. The next 1000 rows of data will be copied to the clipboard.
When the last group of data has been copied to the clipboard, a message box will notify.
Option Explicit
Sub progressiveCopy()
Dim m As Long
Static i As Long, k As Long
Application.CutCopyMode = False
m = 1000
If k <> ActiveCell.Column Then
k = ActiveCell.Column
i = 0
End If
With Worksheets("sheet1")
m = Application.Min(m, .Cells(.Rows.Count, k).End(xlUp).Row - i)
.Cells(i + 1, k).Resize(m, 1).Copy
i = i + m
If i >= .Cells(.Rows.Count, k).End(xlUp).Row Then
MsgBox "This column is complete. Select another column next time."
End If
End With
End Sub
You may wish to set up a hot-key combination for the sub procedure in the Macros dialog to ease repetitive operations.
This code finds the number of rows in the currently selected column. Checks if the number to be copied is less than the rows left. Selects the range and puts the range in copy mode. After the range is copied you would have to go to they sheet, document, or whatever to paste the data.
I've modified the code to then select down 1000 rows or what ever is left in the column. So you should be able to run the code, paste the data, run the code, paste the data. When you hit the end, a message tells you that you are at the end of the column.
Application.CutCopyMode = False
numRowstoCopy = 1000
varCurrentRow = ActiveCell.Row
varCurrentColumn = ActiveCell.Column
FinalRow = Cells(Rows.Count, varCurrentColumn).End(xlUp).Row
varRowsToEnd = FinalRow - varCurrentRow
If varRowsToEnd < numRowstoCopy Then
Range(Cells(varCurrentRow, varCurrentColumn), Cells(varCurrentRow + varRowsToEnd, varCurrentColumn)).Select
Selection.Copy
MsgBox "Last Rows to Paste Have been copied"
Else
Range(Cells(varCurrentRow, varCurrentColumn), Cells(varCurrentRow + numRowstoCopy - 1, varCurrentColumn)).Select
Selection.Copy
ActiveCell.Offset(numRowstoCopy, 0).Select
End If
Edit: I only have 28,000 columns, and you all are correct that they can't all fit in one worksheet. I was testing my code with only a portion of the data and hadn't yet realized that it will not all fit
I have 28,000 columns of data. I am trying to to copy specific columns 5,12,19,26...(ie for i=1:4000, column number = 7*(i-1) + 5). My original thought is below, but the problem is that after each iteration of the loop, the previous column is deselected. So the code below does not copy the intended data.
For i = 1 To 4000
DataSheet.Columns(7 * (i - 1) + 5).Select
Next i
Selection.Copy
ResultsSheet.Paste
I thought about the alternative below (which works, but very slowly), but I was hoping I could write something that executes more quickly (part of the problem is the code selects the destination sheet and pastes each column individually, essentially quadrupling the number of steps as something similar to the first solution).
For i = 1 To nSymbols
DataSheet.Columns(7 * (i - 1) + 5).Copy
ResultsSheet.Select
Columns(i+1).Select
ActiveSheet.Paste
Next i
Any ideas on how to make this code run (faster)?
Use Union and increment your For ... Next by 7 for each increment.
dim c as long, rng as range
with worksheets("sheet1")
set rng = intersect(.columns(5), .usedrange)
for c = 12 to 4000 step 7
set rng = UNION(rng, intersect(.columns(c), .usedrange))
next c
end with
debug.print rng.address(0, 0)
rng.copy destination:=ResultsSheet.cells(2, 1)
I've added Intersect with UsedRange to reduce the full column references. Due to the Union'ed range, this Copy & Paste resjults in a Copy, Paste Special, Values and fORMATS.
I have a series of macros that will insert data pertaining to which button is selected. The problem that I am having is that I have a series of 5 buttons each running a macro containing a different number of rows of data. After the data is entered from the macro, I have the same 5 buttons for the user to select another group of data. Since each macro is a different number of rows it keeps screwing up the formating (example: the 2nd set of macros is set to run in row 3, but when the first set is selected the data continues until row 5). Any advise on how to fix this?
Here is my macro's code:
Sub Realestate()
Sheet14.Unprotect Password:="Loan101"
Range("AV1:CP20").Select
Selection.Copy
ActiveWindow.ScrollColumn = 4
ActiveWindow.ScrollColumn = 3
ActiveWindow.ScrollColumn = 2
ActiveWindow.ScrollColumn = 1
Range("A11:AU11").Select
Selection.Insert Shift:=xlDown
Range("U13:AA13").Select
Application.CutCopyMode = False
ActiveCell.FormulaR1C1 = "=Application!R[10]C[3]"
Range("I14:Q14").Select
ActiveCell.FormulaR1C1 = "=Application!R[119]C[9]"
Range("K24:O24").Select
ActiveCell.FormulaR1C1 = "='Consumer Loan Request'!R[-4]C[-1]"
Range("P24").Select
Rows("20:20").RowHeight = 3
Rows("30:30").RowHeight = 3
Range("j12:w12").Select
Sheet14.Protect Password:="Loan101"
End Sub
The code needs several fixes to be optimized, but the way SO works is to answer one question each time so that future users will find it useful as well. I will hence, in this thread, limit to answer your question: "How can I make a macro starting from the row where the previous one ended?"
The trick is to store the value of the row in a global variable, i.e. a variable whose scope is not limited to the single macro but to the whole module. In order to reach this (please adapt this small example to your code, since nobody can see the project so this can only be a hint):
1) Declare the global variable on top of all macros:
Dim rowStart As Long
2) Write your macros:
Dim rowStart As Long
Sub Macro1()
'code
End Sub
Sub Macro2()
'code
End Sub
So, here's the hint. Make your first macro perform some operations, for example putting 20 random numbers into the first 20 cells of the column "B":
Sub Macro1()
For j = rowStart To rowStart + 20 'rowStart has not been modified yet, so it's equal to 0 by default
Sheets("YourSheet").Range("B" & j+1).Value = Rnd() '"B" & j will be "B1" the first time, "B2" the second time etc.
Next j
rowStart = j 'here we store the value "20", i.e. the last value of j, into the global variable so that we can reuse it after
End Sub
Once this is done, let's say that your second macro will want to add other 10 random numbers to the existing list already created from the first macro. To do this:
Sub Macro2()
For j = rowStart To rowStart + 10 'now rowStart = 20, so we will start from the last row where the previous macro ended!
Sheets("YourSheet").Range("B" & j+1).Value = Rnd()
Next j
rowStart = j 'now we store the value "30", so next time you want to know the last row used, you will know it
End Sub
Unfortunately, your code is very complicated to read and understand what it's doing (I imagine it's recorded since I still see the ActiveWindow.ScrollColumn command coming from you moving up and down your spreadsheet manually).
So, my suggestion is to start from these two tips:
Global variables will help you to keep in memory the last rows touched from your macros;
String merging (Range("B" & j)) will help you to reference objects dinamically.
In general, use the Macro recorder to learn about the commands, but write your own code: it might take a bit longer in the beginning, but you will make sure that the code is flexible and able to adapt to any kind of similar problem. Here's a good tutorial to get started with.