Calculate off sheet? - excel

Can anyone explain what's wrong with the syntax on the line that starts If DBSheet.Range?
Also, I know there is a way to do the calculations "off the sheet" and then spit them back into the sheet? I have an idea on how to do this using .formula and .value, but i am not quite sure how. Can someone explain how i can do this so my calculation is efficient. Thanks!
Dim DBSheet As Worksheet, InSheet As Worksheet, ExSheet As Worksheet, i As Long
Set DBSheet = ThisWorkbook.Sheets("db_main")
Set InSheet = ThisWorkbook.Sheets("interface")
Set ExSheet = ThisWorkbook.Sheets("export")
For i = 2 To 40000
If DBSheet.Range("S" & i) <= InSheet.Range("C20") And DBSheet.Range("S" & i) = InSheet.Range("O15") Then
ExSheet.Range("A" & i) = DBSheet.Range("N" & i)
End If
Next i

Your problem appears to be a type error on your comparison line which also is the first time you reference a Range.
First, verify that the data in those cells is of the datatypes you want (numbers I assume). Second, in my experience Excel will almost always treat ws.Range(...) as a range object which would throw an error in your inequality because it is not just the value you are looking for. You should change that to ws.Range("S" & i).Value or ws.Cells(i, 19).Value. Additionally you probably need to convert that to a number from a string for your comparison using Val() or CDbl() or CInt(). So we have options like:
Val(ws.Range("S" & i).Value)
CDbl(ws.Cells(i, 19).Value)
As far as off sheet calculations, if you are referring to using another worksheet for calculations, you would do basically what you are doing right now just with a 4th worksheet object. If you are referring to calculations in memory, it would be something like...
Dim areaCircle As Double
Dim radius As Double, circumference As Double
Dim ratioOfCircumferenceToArea As Double
radius = CDbl(ws.Cells(i, 19).Value)
areaCircle = radius^2 * 3.14159
circumference = 2 * 3.14159 * radius
ratioOfCircumferenceToArea = circumference / area
Without more information about what you are trying to calculate "off sheet," I can just give you these general cases.

Related

How to multiply a range of values in Excel by a scalar variable using VBA?

I have implemented this method to multiply every array element by a number held in a variable. It is terribly slow.
Is there an accepted "fastest" way to multiply every element in a range by a constant? Or at least one which is not as slow? I have to do this 10 times and it takes a couple of minutes.
MultFactor = 10
For Each cell In Sheet1.Range("B3:B902")
cell.Value = cell.Value * MultFactor
Next cell
The solution cited in Multiply Entire Range By Value? multiplies by a constant (not a variable). If I use this code (changing the range from "A1:B10" to "B3:B902"), I get a nonsense answer.
Dim rngData As Range
Set rngData = Sheet12.Range("B3:B902")
rngData = Evaluate(rngData.Address & "*2")
My original values in B3:B902 are zero for the first 100 elements or so and then increase a bit and finally decrease and have another run of zeros, but what ends up in my range is a series of numbers that clobbers everything in my range. It begins at -224.5 and decreases by 0.5 all the way to the last cell.
-224.5
-224.0
-223.5
etc.
Even if that worked, how would I modify it to use the variable MultFactor?
This will be hundreds to thousands of times faster. The difference is that all of the calcs are done to a VBA array instead of directly to worksheet cells, one by one. Once the array is updated it is written back to the worksheet in one go. This reduces worksheet interaction to just two instances, reading the array and writing it. Reducing the number of instances that your VBA code touches anything on the worksheet side is critical to execution speed.
Sub Mozdzen()
Const FACTOR = 10
Const SOURCE = "B3:B902"
Dim i&, v
v = Sheet1.Range(SOURCE)
For i = 1 To UBound(v)
v(i, 1) = v(i, 1) * FACTOR
Next
Sheet1.Range(SOURCE) = v
End Sub
Building on the above idea, a better way to manage the code is to encapsulate the array multiplication with a dedicated function:
Sub Mozdzen()
Const FACTOR = 10
Const SOURCE = "B3:B902"
With Sheet2.Range(SOURCE)
.Value2 = ArrayMultiply(.Value2, FACTOR)
End With
End Sub
Function ArrayMultiply(a, multfactor#)
Dim i&
For i = 1 To UBound(a)
a(i, 1) = a(i, 1) * multfactor
Next
ArrayMultiply = a
End Function
You need:
rngData = Sheet12.Evaluate(rngData.Address & "*2")
since the address property doesn't include the sheet name by default (so your formula is evaluated in the context of the active sheet's range B3:B902)
Then it would need:
rngData = Sheet12.Evaluate(rngData.Address & "*" & MultFactor)
to add in your variable.

Is there a way to pass a string into the Average function?

I have a worksheet containing a column for the velocity of a vehicle, and I want to take specific ranges from that column (ranges where a turn occurs) and calculate the average speed of all turns combined.
I already have the turn ranges in two arrays, one for the turn starts and one for the turn ends. The code I wrote for this purpose looks like this:
Dim strTurnAvg As String
Dim k As Long
strTurnAvg = ""
For k = LBound(TurnStarts) To UBound(TurnStarts)
If TurnStarts(k) <> TurnEnds(k) Then 'the code for detecting turns rarely messes up and takes the same cell as the start and end of a turn. This is used to ignore such instances
If strTurnAvg = "" Then strTurnAvg = "Range(" & TurnStarts(k) & ":" & TurnEnds(k) & ").Value" Else strTurnAvg = strTurnAvg & ", Range(" & TurnStarts(k) & ":" & TurnEnds(k) & ").Value"
End If
Next
Dim result As Double
result = Application.WorksheetFunction.Average(strTurnAvg) '<- This results in a Run-time error '1004', Unable to get the Average property of the WorksheetFunction class
I tried manually putting the strTurnAvg result into an Average command, and everything worked perfectly fine, but when I try to pass it using the variable, it gives me the runtime error.
Is there a way to achieve this process in another way, or am I doing something wrong?
Try the following:
Dim TurnAvg As Range
Dim k As Long
For k = LBound(TurnStarts) To UBound(TurnStarts)
If TurnStarts(k) <> TurnEnds(k) Then 'the code for detecting turns rarely messes up and takes the same cell as the start and end of a turn. This is used to ignore such instances
If TurnAvg Is Nothing Then
Set TurnAvg = Range(TurnStarts(k), TurnEnds(k))
Else
Set TurnAvg = Union(TurnAvg, Range(TurnStarts(k), TurnEnds(k)))
End If
End If
Next
Dim Result As Double
Result = Application.WorksheetFunction.Average(TurnAvg)
It collects all the ranges using Union into TurnAvg. So you work with the actual ranges and the data in them. You cannot work with strings because you can only calculate with numbers but not with text. The Average functions needs numbers to calculate.
You might want to specify in which workbook/worksheet your ranges are like
ThisWorkbook.Worksheets("Sheet1").Range(TurnStarts(k), TurnEnds(k))
Otherwise VBA might pick the wrong workbook or wrong worksheet.

Trouble running a macro which selects every second column in a range and inputs the result in a formula

I have a macro that selects every second column and inputs the addresses in to an excel defined function:
Dim calcrange As Range
Dim c As Long
Set calcrange = Range("InvestmentOutlay")
For c = 3 To Range("C57").End(xlToRight).Column Step 2
Set calcrange = Union(calcrange, Cells(57, c))
Next
Range("IRR").Formula = "=IRR((" & calcrange.Address & "))"
This code works, however, when I try to run this code:
Dim npvRange As Range
Dim n As Long
Set npvRange = Range("C57")
For n = 3 To Range("C57").End(xlToRight).Column Step 2
Set npvRange = Union(Range("C57"), Cells(57, n))
Next
Range("NPV").Formula = "=NPV((EconGrowth1," & npvRange.Address & "))"
I get an "Application defined or object defined error". The debug highlights the last line of code. I'm still pretty new with VBA, and am unsure what is causing this error, and how to fix it. Any assistance would be greatly appreciated, thanks,
The brackets you are placing around both the EconGrowth1 and npvRange values is making them into one parameter, but EconGrowth1 should be the first parameter, and the other cells should be the second parameter.
So you need to move your brackets:
Range("NPV").Formula = "=NPV(EconGrowth1,(" & npvRange.Address & "))"
And, as SJR mentioned in a comment, your Union should be changed to:
Set npvRange = Union(npvRange, Cells(57, n))
And, as you have already initialised npvRange to be C57, you can start the loop at column 5.
The final code could look like:
Dim npvRange As Range
Dim n As Long
Set npvRange = Range("C57")
For n = 5 To Range("C57").End(xlToRight).Column Step 2
Set npvRange = Union(npvRange, Cells(57, n))
Next
Range("NPV").Formula = "=NPV(EconGrowth1,(" & npvRange.Address & "))"
SJR has just pointed out in a comment that the NPV formula doesn't even need brackets because, unlike the IRR formula which only takes one parameter for values, the NPV formula has parameters of rate, value1, [value2], ....
So that means the Formula line can be just:
Range("NPV").Formula = "=NPV(EconGrowth1," & npvRange.Address & ")"

Excel VBA: Unlimited Range Inputs With Versatility (?)

Could someone help me create a function that will handle an unlimited number of diverse ranges? I have tried "Paramarray variables() as Variant" and "variable as Range" in my arguments list but neither of them provide the versatility that I am looking for.
The key is that I want my function to be able to simultaneously handle things like "MyFunc(A1:A10, B1)" or "MyFunc(A1, B1:10, C11)". The problem I'm finding is that "ParamArray" can only handle comma separated inputs while "variable as Range" can only handle non-comma separated inputs.
Basically, I want to have the same functionality that the SUM() function has. Where SUM can handle an infinite (sort of) number of inputs regardless if they are separated by commas or are in a range.
As requested, here is my code:
Function COMMA_DELIMITER(inputs as Range)
' this function basically concatenates a consecutive set of cells and places commas between values
For Each j in Inputs
stringy = stringy & j.value & chr(44)
Next
stringy = Left(stringy, Len(stringy) - 1)
COMMA_DELIMITER = stringy
End Function
or
Function COMMA_DELIMITER_A(ParamArray others())
'this is the same function, only the cells don't have to be consecutive
For i = 1 to UBound(others) + 1
stringy = stringy & others(i-1) & chr(44)
Next
COMMA_DELIMIERTER_A = Left(stringy, Len(stringy) - 1)
End Function
I pretty much want to create a function that has the flexibility to handle both consecutive cells and/or non-consecutive cells. The inputs would look like this, "=MyFunc(A1, B1:B10, C11, D12:D44)".
Could someone help me create a function that can handle something like this, "MyFunc(A1, B1:B10, C11, D12:D44)"?
Thanks,
Elias
Actually it is possible to do that, and code from chris neilsen is almost there.
Function MyFunc1(ParamArray r()) As Variant
Dim rng As Range
Dim i As Long
Dim j As Variant
Dim s As String
For i = LBound(r) To UBound(r)
For each j in r(i)
s = s & " " & j.Address
Next
Next
MyFunc1 = s
End Function
See? You only have to put one more loop, so if you have a range like [A1:A4] it will loop for into that too. One loop will return another ParamArray, so you have to loop for twice. And if you have just [A1] that's not a problem either, the double looping will cause no problem.
I think there are two issues with your approach
, and   (comma and space) are the Union and Intersect operators for ranges
To pass a multi area range into a single parameter, you need to enclose it in ( )
To demonstrate
ParamArray version
Loop through the array variable to access to individual ranges
Function MyFunc1(ParamArray r()) As Variant
Dim rng As Range
Dim i As Long
Dim s As String
For i = LBound(r) To UBound(r)
s = s & " " & r(i).Address
Next
MyFunc1 = s
End Function
 
Range version
Iterate the range Areas collection to access individual ranges
Function MyFunc2(r As Range) As Variant
Dim rng As Range
Dim i As Long
Dim s As String
For Each rng In r.Areas
s = s & " " & rng.Address
Next
MyFunc2 = s
End Function
Both
=MyFunc1(B5:D5 C4:C6,B10:E10,D13:D16)
and
=MyFunc2((B5:D5 C4:C6,B10:E10,D13:D16))
will return
$C$5 $B$10:$E$10 $D$13:$D$16
(note that the intersection of B5:D5 and C4:C6 is C5)

How to merge multiple Range objects, into one, for use as Chart source

I'm trying to make a chart, with multiple columns as source area.
Basically, I want to select specific columns, where I skip some columns, and merge them all into one range. I've setup a loop, where I create a range, and append it's address to a string, and seperates them with a comma. I'm pretty sure this is how Excel wants it formatted.
BUT, I cannot seem to create a new range from this string.
I hope someone here can help me out.
I would very much like to avoid, having to copy the columns to a new sheet, and just mark it all as a range.
I have the following code, for making the combined range:
'Loops for each number of sections
For Z = 1 To Sheet1.txtNoSections
'Get gauge to use
Section = Workbooks(ThisWorkbook.Name).Worksheets(1).Cells(26 + Z, 6).Value
'Sets varibel for distance from root
Dist = Workbooks(ThisWorkbook.Name).Worksheets(1).Cells(26 + Z, 3).Value
'Get range to use
Set ChartRange = ActiveSheet.Range(ActiveCell, ActiveCell.Offset(rc, Section))
RangeString = RangeString & ChartRange.AddressLocal
If Z <> 1 Then
RangeString = RangeString & ","
End If
Next Z
I have then tried to get a new range with something like this, but no luck.
Dim ActualRange As Range
Set ActualRange = ActiveSheet.Range(RangeString)
When printing the RangeString, it looks like this:
$S$2$V$6181$S$2:$X$6181,$S$2:$Z$6181,$S$2:$AB$6181,$S$2:$AD$6181,$S$2:$AF$6181,$S$2:$AH$6181,$S$2:$AJ$6181,$S$2:$AL$6181,$S$2:$AN$6181,$S$2:$AP$6181,$S$2:$AR$6181,$S$2:$AT$6181,$S$2:$AV$6181,$S$2:$AX$6181,$S$2:$AZ$6181,$S$2:$BB$6181,$S$2:$BD$6181,$S$2:$BF$6181,$S$2:$BH$6181,$S$2:$BJ$6181,$S$2:$BL$6181,$S$2:$BN$6181,$S$2:$BP$6181
Seems like the same union would do.
As discussed in the comments above, the best way to handle this is to use native VBA functions such as Union.
You can find several references on how to use this:
on Daily dose of Excel
on vba Express
even a "better" Union on Chip Pearson's website
Yet, please note that you can answer you own question (it is even highly recommended) and accept it. This way, you can share your knowledge with the community and the way you've solved your issue with your own code.
IMHO, this would be even better than accepting my answer.
Following JMax's guidance, I ended up using Union.
This is the code I ended up with.
The first time through the loop, I set the CombinedRange to my actual range, and the subsequent runs, I union.
For Z = 1 To Sheet1.txtNoSections
'Get gauge to use
Section = Workbooks(ThisWorkbook.Name).Worksheets(1).Cells(26 + Z, 6).Value
'Get range to use
Set ChartRange = ActiveSheet.Range(ActiveCell, ActiveCell.Offset(rc, 0))
Debug.Print "ChartRange(" & Z & "): " & ChartRange.Address
If Z = 1 Then
Set CombinedRange = ChartRange
Else
Set CombinedRange = Union(CombinedRange, ChartRange)
End If
ActiveCell.Offset(0, 5).Activate
Next Z
Debug.Print "Combined: " & CombinedRange.Address

Resources