Excel 2016 UDF not showing up - vba

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

Related

How do I have my code recognize cells with a specific string STRUCTURE, while ignoring others?

So, here's what I'm facing.
I have a bit of vba code, that will find every cell in a single column containing a specific string. The words it looks for are in an array called totArray. This works well.
Problem is, when a word in the cell contains "SUBTOTAL" or something similar, it will still find it and copy it. Also, there will be words where TOTAL or TOTAAL aren't followed by numbers of unknown length.
Please see my code attached. How do I get it so that it will find every case where the cell contains TOTAL or TOTAAL but followed always by a non-common structure of numbers only.
Sub CopyValues()
Dim totArray As Variant
Dim wsSource As Worksheet
Dim wsDest As Worksheet
Dim NoRows As Long
Dim DestNoRows As Long
Dim I As Long
Dim J As Integer
Dim rngCells As Range
Dim rngFind As Range
Dim Found As Boolean
totArray = Array("TOTAAL ", "TOTAL")
Set wsSource = Worksheets("Input")
Set wsDest = Worksheets("Output")
NoRows = wsSource.Range("A65536").End(xlUp).Row
DestNoRows = 2
For I = 1 To NoRows
Set rngCells = wsSource.Range("A" & I)
Found = False
For J = 0 To UBound(totArray)
Found = Found Or Not (rngCells.Find(totArray(J)) Is Nothing)
Next J
If Found Then
rngCells.Copy wsDest.Range("B" & DestNoRows)
DestNoRows = DestNoRows + 1
End If
Next I
End Sub
To find a string with characters before/after the specified string:
You could put wildcards into the string, see this documentation.
"*TOTAL" would find Subtotal, Grandtotal etc as the asterisk wildcards any number of characters.
"*TOTAL????? would find any word with total at the end (like the example above) and with up to 5 characters after the word (as there are 5 question marks). For example: Subtotal123 or Subtotal54321
*TOTAL ????? would find Subtotal 123 or Subtotal 54321 (notice the space can be used in the string between characters/wildcards).
Using this info you should be able to adjust your Array strings to work in your situation.
To find an exact match to the specified string:
You should specify the LookAt parameter in your .find method.
e.g. rngCells.Find(totArray(J), , , LookAt:= xlWhole).
Using the LookAt function is pretty straightforward.
xlWhole means the search value must match the entire cell contents.
xlPart means the search value only has to match part of the cell
Note: In that example the After and LookIn parameters are omitted.
The LookAt parameter is the 4th parameter which is why there are blanks between the commas.
If you don't specify your parameters, Excel uses the parameters from the last search you ran either from using the .Find method via VBA or the GUI find window.
You can read more about each parameter, including the LookAt parameter in this article about the vba find method.

Looping through columns to get column numbers based on headers

I have a template with a set number of columns (170) and title headers (row 1 cell name's). This is always the same, until users add columns in between (they're instructed not to change headers). The idea is to make it tamperproof as far as the adding of columns is involved.
I'd like to make variables to hold some of the headers (with the capacity to hold all) and check these with the template to find out the column number (in a loop I reckon). It's probably wisest to make a function to call upon it?
Dim ColHeader1Str as string 'literal row 1, column 1 value (which is always
'the same string and position in the template)
Dim iColHeader1 as integer 'holds the (to be set) value of the column number
Set ColHeader1Str = "ColHeader1"
Now I'd like a loop where it loops trough all the columns (last column = 200) and checks to see what the column number is that matches the ColHeader1Str and store this in the iColHeader1
So something like:
Function find_columnNmbr
Dim i As Integer
For i = 1 To 200 Step 1
If 'ColHeader1Str matches actual column header name
'set found integer as iColHeader1 and so forth
Exit For
End If
Next
End Function`
I know I'm missing a few steps and I'm hoping you guys can help me out.
Update: The template has set column headers. When users interact with it a result could be that columns shift position, or they add more. I have a workbook that needs to load data out of the user's altered template.
I.E. The template has columns 1, 2, 3, 4 and the names are column1, column 2 etc. A user ads a random column so now there are 5. The loop needs to loop through the names of the column headers and identify the column number of the original template columns 1, 2 etc based on a string variable with the original names, which I've hard coded beforehand. These are public constants.
What function LookForHeaders do: input a string, then search for the string in usersheet.range(1:1). If it is found, return the column number of that cell, otherwise it returns 0.
Private Function LookForHeaders(ByVal headerName As String) As Long
Dim rng As Range
Dim userSheet As WorkSheet
Set userSheet = 'userSheet here'
On Error GoTo NotFound
LookForHeaders = userSheet.Range("1:1").Find(headerName).Column
Exit Function
NotFound:
LookForHeaders = 0
End Function
Private Sub Test()
Dim rng As Range
Dim template As WorkSheet
Set template = 'template here'
For Each rng In template.Range(Cells(1,1), Cells(1,200))
iColHeader1 = LookForHeaders(rng.Value)
'Do something with iColHeader1
Next rng
End Sub
Not sure what your looking for but here is example
Option Explicit
Public Sub Example()
Dim LastCol As Long
Dim i As Long
LastCol = ActiveSheet.UsedRange.Columns(ActiveSheet.UsedRange.Columns.Count).Column
For i = 1 To LastCol
If Cells(i) = "Name" Then
Debug.Print Cells(i).Address
End If
Next
End Sub

VBA: make calculation - output just value

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

EXCEL VBA - how to pass # of filled cells inside a custom range, into the "i" variable using Application.WorksheetFunction.CountA?

I need to assign a number to the "i" variable.
This # should be the number of filled cells inside the Worksheets("Offset").Range("AN20:AN3000")
Yes - "Offset" is in this case the name of the sheet.
Here is what I exactly need.
Inside aforementioned range there is 171 filled cells, and that number I need to assign to the "i" variable.
Preferably, I'd like to achieve that using Application.WorksheetFunction.CountA
This is what I've managed to code myself
Sub celebrities_height_into_array()
' declare an array to hold celebrities height
Dim height_array() As Integer
Dim height_range As Range
Dim i As Integer
Set height_range = ThisWorkbook.Worksheets("Offset").Range("AN20:AN3000")
' I keep celebrities height in this range, in centimeters.
i = Application.WorksheetFunction.CountA(Range(height_range))
End Sub
Later I'm going to load all heights into the array.
This is what I've read about using WorksheetFunction.CountA
This is how my sheet looks like.
And here is the end of the Range.
Your only mistake is the extra Range(
Try the following code. It should work.
Sub celebrities_height_into_array()
' declare an array to hold celebrities height
Dim height_array() As Integer
Dim height_range As Range
Dim i As Integer
Set height_range = ThisWorkbook.Worksheets("Offset").Range("AN20:AN3000")
' I keep celebrities height in this range, in centimeters.
i = Application.WorksheetFunction.CountA(height_range)
End Sub
height_range is already defined as a range.

Vectorial formula for cell validation in Excel using VBA

I am writing a VBA formula to check that all characters in a cell "TestChars" are allowed, where allowed means that each character appears in a list defined by another cell "AllowedChars". To make things even harder, I would like this formula to work on ranges of cells rather than on a single cell.
The current code seems to work:
Option Explicit
Public Function AllCharsValid(InputCells As Range, AllowedChars As String) As Boolean
' Check that all characters in InputCells are among
' the characters in AllowedChars
Dim Char As String
Dim Index As Integer
Dim RangeTestChars As Range
Dim TestChars As String
For Each RangeTestChars In InputCells
TestChars = RangeTestChars.Value
For Index = 1 To Len(TestChars)
Char = Mid(TestChars, Index, 1)
If InStr(AllowedChars, Char) = 0 Then
AllCharsValid = False
Exit Function
End If
Next Index
Next RangeTestChars
AllCharsValid = True
End Function
I have the following questions:
The formula takes a range and returns a single boolean. I would prefer a vectorized function, where, given an input range, you get a corresponding range of booleans. It seems like built-in formulas like 'EXACT' can do this (those formulas where you have to press ctrl-shift-enter to execute them and where you get curly-brackets). Is there a way to do that with user-defined functions?
I am not new to programming, however I am completely new to VBA (I started literally today). Is there any obvious problem, weirdness with the above code?
Are there special characters, extremely long texts or particular input values that would cause the formula to fail?
Is there an easier way to achieve the same effect? Is the code slow?
when you start typing built-in formulas in excel you get suggestions and auto-completion. This doesn't seem to work with my formula, am I asking for too much or is it possible to achieve this?
I realize that this question contains several weakly related sub-questions, so I would be very happy also with sub-answers.
The following code will return a range of boolean values offset one column from the initial input range. Simply create a new tab in Excel and run testAllCharsValid and show the Immediate window in the IDE to see how it works.
Sub testAllCharsValid()
Dim i As Integer
Dim cll As Range, rng As Range
Dim allowedChars As String
' insert test values in sheet: for testing purposes only
With ActiveSheet ' change to Thisworkbook.Sheets("NameOfYourSheet")
Set rng = .Range("A1:A10")
For i = 1 To 10
.Cells(i, 1) = Chr(i + 92)
Next i
End With
' fill allowedChars with letters a to z: for testing purposes only
For i = 97 To 122
allowedChars = allowedChars & Chr(i)
Next i
' get boolean range
Set rng = AllCharsValid(rng, allowedChars)
' check if the returned range contains the expected boolean values
i = 0
For Each cll In rng
i = i + 1
Debug.Print i & " boolean value: " & cll.Value
Next cll
End Sub
' Check that all characters in InputCells are among
' the characters in AllowedChars
Public Function AllCharsValid(InputCells As Range, allowedChars As String) As Range
Dim BoolTest As Boolean
Dim Char As String
Dim Index As Integer
Dim RangeTestChars As Range, RangeBooleans As Range, RangeTemp As Range
Dim TestChars As String
For Each RangeTestChars In InputCells
BoolTest = True
TestChars = RangeTestChars.Value
For Index = 1 To Len(TestChars)
Char = Mid(TestChars, Index, 1)
If InStr(allowedChars, Char) = 0 Then BoolTest = False
Next Index
Set RangeTemp = RangeTestChars.Offset(0, 1) ' change offset to what suits your purpose
RangeTemp.Value = BoolTest
If RangeBooleans Is Nothing Then
Set RangeBooleans = RangeTestChars
Else
Set RangeBooleans = Union(RangeBooleans, RangeTemp)
End If
Next RangeTestChars
Set AllCharsValid = RangeBooleans
End Function
cf 2) If the length of the test string is zero, the function will return True for the cell in question, which may not be desirable.
cf 3) There is a limit to how many characters an Excel cell can contain, read more here. I suppose, if you concatenated some very long strings and sent them to the function, you could reach the integer limit of +32767, which would cause a run-time error due to the integer Index variable. However, since the character limit of Excel cells is exactly +32767, the function should work as is without any problems.
cf 4) None that I know of.
cf 5) This is not the easiest thing to achieve, but there is help to be found here.