VBA turning sections of a recorded macro into something more efficcient - vba

I am trying to optimise some data sorting code, the original code looks like it has been "written" through the use of excels "Record Macro" function.
The following is an example from the code.
Range("A12").Offset(z, 0).Select
Selec.Resize(Selection.Rows.Count, Selection.Columns.Count + (X2 - 1)).Select
Selection.Copy
Range("C4").Offset(y, 0).Select
ActiveSheet.Paste
In order to make this code more efficient, should i re-write this section to involve a range variable that its' .Value is equal to the Selection data?
The X2, z and y variables are used as part of the copy function and are slowly increased to make reading the end product much easier. They are altered elsewhere in the module.
I would just like to see some suggestions for what you think I should do.
I have read the very popular "How to avoid using Select in Excel VBA Macros" question on here, and am just looking for some further advice.
(How to avoid using Select in Excel VBA macros)
Thanks

I guess this would be the easiest way.
Range("C4").Offset(y, 0).Resize(1, x2) = Range("A12").Offset(z, 0).Resize(1, x2)
Although I would advise:
to avoid .Offset and work with named ranges instead
to avoid .Copy and .Paste sometimes your data might be too big for your clipboard and using just the =-operator is way faster
to not name your variables with capital letters or additional ciphers, it can be very confusing
use this format .Cells( row, column ) instead of `.Cells("A1"). It's easier to handle and can be changed more easily via calculations
HTH

Copy/pasting isn't very efficient. Doing Range("X1").value = Range("Y1").value is better. This is what I would do
'change this
Selection.Copy
Range("C4").Offset(y, 0).Select
ActiveSheet.Paste
'to this
Range("C4").Offset(y, 0).Value = Selection.Value 'selection value is the range that is being copied.

Related

Excel macro to normalize data

I am currently trying to create a macro for Excel in which a column containing certain values (numbers basically) will be displayed in a new column in a normalized way (highest number = 1, lowest number = 0).
Usually, I would just use the formula:
=(J2-MIN($J$2:$J$XXX))/(MAX($J$2:$J$XXX)-MIN($J$2:$J$XXX))
However, as the length of the column is dynamic and will change for each set of values, I cannot enter a value for XXX.
Now, I found out how to have a dynamic range (e.g.: numRows = Range(Selection, Selection.End(xlDown)).Rows.Count) but I did not manage to merge both functions.
I found a thread already in this site about normalization of data but I think it was a bit of a different story and this one here should be simpler.
I would appreciate any help! As I just started working with macros (2h ago) I would also appreciate if this will be in simple language.
EDIT:
First of all, thanks for the quick reply!
I naively tried making it work with this code:
Sub Normalize_TEST()
'
' Normalize_TEST Makro
'
'
Range("A1").Select
numRows = Range(Selection, Selection.End(xlDown)).Rows.Count
Range("K2").Select
ActiveCell.FormulaR1C1 = "=(("J2")-MIN($J$2:$J$numRows$))/((MAX($J$2:$J$numRows$)-MIN($J$2:$J$numRows$))
Range("K2").Select
Selection.AutoFill Destination:=Range(Cells(2, 11), Cells(numRows, 11))
End Sub
But it is not working and I get an error message ("error of compiling").
I just realize now that you are absolutely right, I don't even need VBA. Your line of =(J2-MIN($J:$J))/(MAX($J:$J)-MIN($J:$J)) works fine. I wasn't aware that with $j:$J it realizes to not include empty cells.
I simply used this code now for cell K2 and then did a VBA autofill function for the rest.
I think this can be closed.
Thank you #tigeravatar for your super quick help!

VBA Paste values - Response time

I have a code that copies one range to another
rng1.Copy Destination:=rng2
However I only want the values, not all the formatting, so I have the below which works to achieve this
rng1.Copy
Application.ScreenUpdating = False
rng2.PasteSpecial Paste:=xlPasteValues
Application.ScreenUpdating = True
However, the processing speed of this is painfully slow. The sheet grinds to a halt for quite a long time compared to the instant response of the 1st formula.
Is there a method of amending the 1st formula to paste values only? I was unable to find a way in similar questions about pasting values....
Alternatively, is there another method that is quicker than using the 2nd formula?
copy & paste is notoriously slow in VBA, better to just assign directly, assuming both ranges are the same size
rng2.value = rng1.value

Optimizing VBA copy/paste functions and loops

I have a large Excel sheet. I'm interested in moving data from one cell to another. I have researched on how to optimize the copy/paste function to take as little time as possible and found that:
Sheet1.Range("A1").Value = Sheet1.Range("B1").Value
or
Sheet1.Range("A1").Copy Destination:=Sheet1.Range("B1")
is faster than:
Sheet1.Range("A1").Copy
Sheet1.Range("A1").PasteSpecial xlPasteValues
Application.CutCopyMode=False'Clear Clipboard
Now, I need to make a copy with the following options:
Range("A1").Select
Selection.Copy
Range("B1").Select
Selection.PasteSpecial Paste:=xlValues, Operation:=xlNone, SkipBlanks:= _
False, Transpose:=False
Is it possible to translate that into the code similar to one of the previous mentioned?
Also, in regard to loops. Is it generally faster to avoid them (if possible) or how is the Compiler optimized? Let me give you an example so you don't misunderstand me:
Lets say I want to Copy values from "A1:A200" to "B1:B200". Which method would then be better in general?
Dim counter As Integer
For counter = 1 To 200
Sheet1.Range("A" & CStr(counter)) = Sheet1.Range("B" & CStr(counter))
End counter
Or is it better to simply use the code:
Sheet1.Range("A1:A200").Copy Destination:=Sheet1.Range("B1:200")
These examples are of cause grossly simplified, though if someone could help me understand the principles applied, it would be of great value to me!
First of all don't use Select and Selection. This really slows your code. Especially in loops.
The fastest way to copy values is to simply do this Range("B1:B99").Value=Range("A1:A99").Value
Furthermore you should deactivate Screenupdating and especially if formulas referr to the paste area switch off the automated calculation of the worksheet: Application.ScreenUpdating=False
Application.Calculation=xlCalculationManual
In total it should somehow look like this:
Sub CopyFast ()
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
ActiveSheet.Range("B1:B99") = ActiveSheet.Range("A1:A99")
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
End Sub
But this really gets important when copying huge amounts of values e.g. in loops instead of the simple copying task in this example.

Re: .Copy and .Paste in Excel VBA

Why, when copying a cell, shape, etc in Excel VBA, is ActiveSheet.Cells(i,j).Paste not valid?
Instead I have
Cells(i,j).Select
ActiveSheet.Paste
which works, but why?
Because .Paste has to be applied to a SheetObject (as you have written correctly: ActiveSheet.Paste)
Check out the method on MSDN. It has to be used the following way:
Worksheets("Sheet1").Range("C1:C5").Copy
ActiveSheet.Paste Destination:=Worksheets("Sheet1").Range("D1:D5")
Or more shortly:
Cells(j,i).Copy Destination:=Cells(y,z)
Or use the PasteSpecial-method. It can be applied to Range-objects:
With sheet
.Range("C1:C5").Copy
.Range("D1:D5").PasteSpecial Operation:=xlPasteSpecialOperationAdd
End With
You don't have to tell it to paste for example,
if you have the variables set for i and j then you can use this
Range("A1").Copy Cells(i, j)
You could even have no copying at all, such as:
Cells(i, j)=Range("A1")

Macro Copy&Paste

I'm trying to create a macro that will copy data from one worksheet and place into another. This I can do with no problem. But, when I want to use the same macro in another row is where I have my problem. Basically what I want to do is copy cell D11 from sheet1 and place that in cell B4 on sheet2, etc (What I'm doing is obviously more complicated than that, but that doesn't matter here).
My problem is when I want to now run this macro and copy cell D12 from sheet1 and paste into B5 on sheet2 the value pasted jumps to B4. I understand that this happens because of where the VBcode is saying to paste the copied value.
My question is how to I just have it paste in whatever row I choose? Maybe based on what row/cell I have selected.
Current code, written by recording the macro
Sheets("sheet1").Select
Range("D11").Select
Selection.Copy
Sheets("sheet2").Select
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
Range("B4").Select
I'm assuming the last line is where I need to make the change, but I'm not sure what to change.
Thank you! Any and all help is greatly appreciated.
As a general rule, try to avoid Selection Copy-Paste (detailed discussion is provided in: "Application.Calculation = xlCalculationManual" statement causing run-time error 1004 in VBA Copy-Paste procedure). Instead, use direct copy statement, which will solve you issue and significantly improve performance:
Listing 1.
Sub DirectCopySample()
Application.ScreenUpdating = False
Sheets("Sheet1").Range("D11").Copy Destination:=Sheets("Sheet2").Range("B5")
Application.ScreenUpdating = True
End Sub
Sub in Listing 1 performs direct copy from Cell: Sheets("Sheet1").Range("D11") into cell: Sheets("Sheet2").Range("B5").
Also, your initial Copy-Paste Sub could be simplified (it will also make it work, though Listing 1 is preferred)
Listing 2.
Sub CopyPasteSample()
Sheets("sheet1").Range("D11").Copy
Sheets("sheet2").Range("B5").PasteSpecial Paste:=xlPasteValues
End Sub
Hope this will help. Best regards,
You seem to have recorded a Macro and are trying to replay it. Here is a real VBA code (not a Macro recording type):
Sheets("sheet2").Range("B5") = Sheets("sheet1").Range("D11").Value
This is all!
BTW, your predicament comes from the fact that the PasteSpecial method copies into the currently selected cell. You've tried running this Macro several times and the Range("B4").Select line did the trick. If you insist on your approach the insert Range("B5").Select BEFORE the PasteSpecial.