VBA: make calculation - output just value - vba

I am very new with VBA and I was hoping someone could help me. I would like to create a macro that makes a calculation but returns just the value. I have two specific examples where I would like to do this:
-One is where the macro calculates the average of A2:A100 and return the Value of that average in B100, then the average between A3:A101 and return the value in B101, and so in until B700
-The second case is where the macro finds cell containing the Max value in cells A2:A101, and return that value in cell E1.
Thanks for the help.

You need User defined Functions instead of Subs.
Then in the needed cell (say, B100) you enter =MyFunc(A2:A100).
In your case, it seems you can copy-paste down.
The assignment could be done at the VBA level also, with Range("B100").Formula = MyFunc(...
This, with a little googling, should quickly get you going.
Among many references out there:
https://support.office.com/en-us/article/Create-custom-functions-in-Excel-2f06c10b-3622-40d6-a1b2-b6748ae8231f

You have two options for doing any sort of calculation (which I assume is going to be more involved than replicating the built-in functionality of AVERAGE(), SUM() or MAX()).
User Defined Function (UDF) - Create a Function that returns a single value (like Double, String or Variant array). This function can take numeric or range arguments, and optionally it can reference values from the active worksheet and returns the calculated value of the cell it was invoked from. Here are two examples:
Manual Calculation - Create a Subroutine that reads values and/or files and manually updates the values of cells before it exits. This method is required when more than one cell needs to written. It also requires a trigger, such as a button to press, or a menu item to click, or some other event to call the subroutine. There is two ways to process a table of cells. One is direct using the .Cell() method, and the other is the faster one taking all the values into an array and writing out all the values with one command.
Example UDF Code (in a Module)
'---------------------------------------------------------------------------------------
' Procedure : MySquare
' Author : ja72
' Date : 10/29/2017
' Purpose : Returns `x^2/c` where c is defined in cell [B2]
'---------------------------------------------------------------------------------------
'
Public Function MySquare(ByVal x As Double) As Double
Dim c As Double
c = [B2]
' Same as: c = ActiveSheet.Range("B2").Value
MySquare = x ^ 2 / c
End Function
'---------------------------------------------------------------------------------------
' Procedure : MyAverage
' Author : ja72
' Date : 10/29/2017
' Purpose : Uses the built-in funcrtion 'Average()` on the input cells
'---------------------------------------------------------------------------------------
'
Public Function MyAverage(ByVal r As Range) As Double
MyAverage = WorksheetFunction.Average(r)
End Function
Example worksheet layout
Example Manual Calculations (in a Sheet)
'---------------------------------------------------------------------------------------
' Procedure : FillMultiplicationTableDirect
' Author : John
' Date : 10/29/2017
' Purpose : Fills a n×n table of cells directly
'---------------------------------------------------------------------------------------
'
Public Sub FillMultiplicationTableDirect(ByVal n As Long)
Dim r_table As Range
' Start from top left cell B15
Set r_table = Range("B15")
' Process the cells one by one
Dim i As Long, j As Long
For i = 1 To n
For j = 1 To n
' Write the resuling cell directly
r_table.Cells(i, j).Value = i * j
Next j
Next i
End Sub
'---------------------------------------------------------------------------------------
' Procedure : FillMultiplicationTableOffline
' Author : John
' Date : 10/29/2017
' Purpose : Fills a n×n table of cells with an array
'---------------------------------------------------------------------------------------
'
Public Sub FillMultiplicationTableOffline(ByVal n As Long)
Dim r_table As Range
' Start from top left cell B15 and grab n×n cells
Set r_table = Range("B15").Resize(n, n)
' Define an array with the values
Dim res() As Variant
' Fill the array with the existing values of the cells
res = r_table.Value
'Process the array (offline calculation)
Dim i As Long, j As Long
For i = 1 To n
For j = 1 To n
res(i, j) = i * j
Next j
Next i
' Write the array back to the worksheet
r_table.Value = res
End Sub
The above functions need to be triggered manually with a command like
Call Sheet1.FillMultiplicationTableDirect(10)
or
Call Sheet1.FillMultiplicationTableOffline(10)
Example Result

Related

Excel 2016 UDF not showing up

I have been taking some VBA course from the Excel for Business web site and created a user defined function called ULookup. The function works fine in the spreadsheet and if I use the = sign, the Ulookup will show up. However, Control-Shift-A does not show the complete fill in hints like it does on a PC and if I go to the insert function menu, the function will not show up at all. Is this a bug in the Mac version of Excel or is am I missing something? Here is the code that I put in Module 1 of myWorkbook:
Option Base 1
Function ULookup(lookup_value As Range, lookup_range As Range, source_range As Range, Optional match_type As Integer = 0) As Variant()
' Performs an Index / Match Lookup.
Dim results_Array() As Variant
Dim lookup_index As Long
ReDim results_Array(1) As Variant
' Consider defining this as an array function
' Dim lookup_value ' Contains the value I want to find
' Dim lookup_range ' Range to search in
' Dim source_range ' Pull corresponding value from
' Dim match_type ' Consider the type of match to perform, exact, lesser, greater
' Find lookup_value in lookup range
lookup_index = Application.WorksheetFunction.Match(lookup_value, lookup_range, match_type)
' Determine if lookup value was in the range.
' Get corresponding value from source range
results_Array(1) = WorksheetFunction.Index(source_range, lookup_index)
' Do not edit code beyond this comment
ULookup = results_Array
End Function

Passing the value and color of the text in a cell to a user defined function [duplicate]

This question already has an answer here:
#Value error while accessing user defined function in VBA
(1 answer)
Closed 5 years ago.
Currently I am experiencing issues with passing the value of a cell and its respective text color to my user defined function. I am passing the references as ranges and using .Font.ColorIndex. This is then used in a IF statement to determine if any are red (value of 3) and then application.caller.fontIndex=3 to turn the cell text red.
Public Function Example(AA As Range, BB As Range) As double
Dim AAcolor, BBcolor As Integer
AAcolor=AA.font.colorindex
BBcolor=BB.font.colorindex
IF BBcolor=3 Or AAcolor=3 Then
Application.caller.font.colorIndex=3
End If
The rest of the code is simply formulas that calculate a double from the ranges inputted which is returned as a double.
To clarify, I am trying to determine the color of the text of the referenced input cells. I am not limited to a UDF to do this if I can call the sub from my UDF.
proof of concept ...
try this ... use F5 or F8 to step through "sub abc123" and watch K5 on worksheet ( I5 is just to supply a variable to the UDF )
it is from a recorded macro, so it is kind of convoluted
note: use another UDF in another cell if you want to do more stuff
Sub abc123()
' run on empty worksheet
Rows("5:5").Delete
Range("K5").FormulaR1C1 = "56.7" ' just some random data
Range("K5").FormatConditions.Add Type:=xlExpression, Formula1:="=J5=3" ' conditional format dependent on value of J5
Range("K5").FormatConditions(Range("K5").FormatConditions.Count).SetFirstPriority
Range("K5").FormatConditions(1).Font.Color = -16776961
Range("K5").FormatConditions(1).Font.TintAndShade = 0
Range("K5").FormatConditions(1).StopIfTrue = False
Range("J5").FormulaR1C1 = "=example(RC[-1])" ' UDF returns value to J5
Stop
Range("I5").FormulaR1C1 = "2" ' just some values passed to UDF
Stop
Range("I5").FormulaR1C1 = "3" ' this one should make K5 go red
Stop
Range("I5").FormulaR1C1 = "4"
End Sub
Public Function Example(a As Variant) As Variant ' UDF
Example = a ' just echo back the value that was received
End Function

Simple moving average range in Excel-VBA

This code is just to calculate simple moving average. Opened an excel, created dummy array in C row from 1 to 20. I want to create a function for eg: SMA(C7,3) = which should give average of C5:C7.
Coming back to VBA after long time, not able to figure whats the error in the below code.
Function sma1(rng As Range, N As Integer)
Set rng = rng.Resize(-N + 1, 0)
sma1 = Application.WorksheetFunction.average(rng)
End Function
avoid using a cell name as a function
fixed the RESIZE()
used an internal range variable
Function smal(rng As Range, N As Integer) As Variant
Dim rng2 As Range
Set rng2 = rng.Resize(N, 1)
smal = Application.WorksheetFunction.Average(rng2)
End Function
EDIT#1:
Based on Scott's comment:
Function smal(rng As Range, N As Integer) As Variant
Dim rng2 As Range
Set rng2 = rng.Offset(1 - N, 0).Resize(N, 1)
smal = Application.WorksheetFunction.Average(rng2)
End Function
I assume you want the column along side it to give you're SMA (as shown below?):
If so, the below will do it and drag it autocomplete it to the bottom of you column C array:
Sub SMA3()
Range("D7").FormulaR1C1 = "=AVERAGE(R[-2]C[-1]:RC[-1])" 'This is a relative reference (left one cell and up two cells) - This give your three inputs
Range("D7").AutoFill Destination:=Range("D7:D" & Range("C1048576").End(xlUp).Row) 'Autofills the SMA
End Sub
Just an FYI this can be done with existing formula:
=IF(ROW(C1)<$E$1,"",AVERAGE(INDEX(C:C,ROW(C1)-$E$1+1):C1))
E1 contains the number of rows to include.

Custom Function Entering the Result Array into a Different Cell

I have created my own function to determine count the values in between to given values in increments of 30 as seen here
Function InBetween(First As Integer, Last As Integer)
Dim i As Long, F As String, a() As String
F = First
For i = First + 30 To Last Step 30
F = F & "|" & i
Next i
InBetween = F
End Function
When I use this function, I currently have it returning the result array in the cell the formula was entered into in the format of "1|2|3|4". Is there a way I can get this array to populate into the cell below the one containing the formula?
Note: I don't want the formula in the cell as I need to refer to the cell in a future equation that will use the result and not the equation.
This was surprisingly difficult. At first I tried calling a sub from the function to affect the cell below using application.caller but this always returned a #value error. It seems a UDF can't run anything that affects the worksheet.
Eventually I came up with this:
Create a worksheet change event by pasting this into the worksheet object in vb:
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
On Error Resume Next
If Left(Target.Offset(-1, 0).Formula, 10) = "=InBetween" Then Call DoX(Target.Offset(-1, 0), InBetween(10, 60))
On Error GoTo 0
End Sub
Then paste this into a module
Sub DoX(r As Range, val As String)
Sheets(r.Parent.Name).Cells(r.Row, r.Column) = ""
Sheets(r.Parent.Name).Cells(r.Row + 1, r.Column) = val
End Sub
Then use your function as normal, but remember to hit return after you enter it so the active cell is the cell below where you entered the formula.

Convert VBA Macro to Function

I have been trying to create a function to retrieve column titles found in row four in an excel sheet. This is what I have so far, can anybody help me please?
Sub Test_Click()
Dim text As String
Dim titles(200) As String
Dim nTitles As Integer
For i = 1 To 199
If Trim(Sheets("Sheet1").Cells(4, i).Value) = "" Then
nTitles = i - 1
Exit For
End If
titles(i - 1) = Sheets("Sheet1").Cells(4, i).Value
Next
For i = 0 To nTitles
Sheets("Sheet1").Cells(20 + i, 1).Value = titles(i)
Next
End Sub
You need to make an array function for this. So your function will take in inputs through a range
Function ReturnArray(Input as Range) as Variant
' Do stuff with the Input range
Dim Output(m,n) as Variant
'Loop through m,n to fill in the output values as you would in a range
ReturnArray = Output
End Function
And when you put in the function in excel, type it in the cell after highlighting where you want the output and press Ctrl-Shift-Return
Just as you write a Sub you can write a Function, just substitute the words at the beginning and at the end of your code.
Now, about how to return the values, obviously it will be an array, so you'll need to declare the array, set its size, fill its cells and return it. This can be done like this:
Function yourFunction() as String()
' You already have an array named "titles" which stores the values you want
' to return. Fill it exactly as you do in your original code.
yourFunction = titles ' This is the way to return the array.
End Function
If you want to use this function in a worksheet (as a formula), remember that this is an array-function, so you'll need to press Ctrl+Shitf+Enter after you enter the function in the cell instead of just [Enter].