User Defined Function Working in Immediate window but nowhere else

D_Miller

New Member
Joined
Dec 17, 2019
Messages
15
Office Version
  1. 2016
Good Morning guys,

I have a User defined Function (UDF) which takes various ranges of cells in a column and compares them. Each range consists of one column only and the objective of the function is to look across these columns and identify the cell with the lowest value and change its color to yellow. The business context is that i can have between 2-5 suppliers with their prices and the aim is to identify the supplier with the lowest price on each item they have quoted on.

The function works fine but the problem is it only works when I call it from the immediate window. '?udf_IdentifyLowestCost("A2:A5", "B2:B5", "C2:C5", "D2:D5")

Outside the VBA editor and on the Excel Data entry area if I call the same UDF by using =udf_IdentifyLowestCost(A2:A5, B2:B5, C2:C5, D2:D5) nothing happens.

Can someone shed some light on this?
 

Excel Facts

Formula for Yesterday
Name Manager, New Name. Yesterday =TODAY()-1. OK. Then, use =YESTERDAY in any cell. Tomorrow could be =TODAY()+1.
Hi & welcome to MrExcel.
Could you please post the code that you are using?
 
Upvote 0
I notice that when you call it in the imediate window, you pass four strings as arguments, but when you call it from the worksheet, you pass four ranges.

What happens when you put =udf_IdentifyLowestCost("A2:A5", "B2:B5", "C2:C5", "D2:D5") in a cell
 
Upvote 0
Good Morning Guys,

Thanks for the welcome Fluff it is greatly appreciated.

Mike as to your question. I am calling it with quotation and without in Excel and it is still not working apart from using the immediate window which works perfectly. There is the code for you to see if anything jumps out.

VBA Code:
Option Explicit


'?udf_IdentifyLowestCost("A2:A5", "B2:B5", "C2:C5", "D2:D5")
'?udf_IdentifyLowestCost("A2:A5","C2:C5")
'?udf_IdentifyLowestCost("L6:L47","P6:P47","T6:T47","X6:X47")

Public Function udf_IdentifyLowestCost(getSupplier_1 As Variant, _
                                        getSupplier_2 As Variant, _
                                        Optional getSupplier_3 As Variant, _
                                        Optional getSupplier_4 As Variant, _
                                        Optional getSupplier_5 As Variant) As String
                                        
' Code created by : Dane A. Miller - https://www.mrexcel.com/board/members/d_miller.454874/#about

Dim rg As Range
Dim sht As Worksheet
Dim arrColNam() As String
Dim i As Long, iUBound As Long
Dim arrHolding() As Variant
Dim arrSorted() As Variant, x As Variant
Dim iColumnNo As Long, iRowNo As Long, iRowCount As Long
Dim j As Long, iCellValue As Double, iCellValue2 As Double
Dim iLowestCellValue() As Variant
Dim cellAddress As String
Dim c As Double
Dim iarrColumName As Integer
Dim outputMsg As String

Dim r1 As Range, r2 As Range, r3 As Range, r4 As Range, r5 As Range
Dim strColumnList As String, colListValue As String
Dim Supplier1ColumnNo As Long, Supplier2ColumnNo As Long, Supplier3ColumnNo As Long, Supplier4ColumnNo As Long, Supplier5ColumnNo As Long
Dim Supplier1ColumnName As String, Supplier2ColumnName As String, Supplier3ColumnName As String, Supplier4ColumnName As String, Supplier5ColumnName As String
Dim SupLowestTotal_1 As Double, SupLowestTotal_2 As Double, SupLowestTotal_3 As Double, SupLowestTotal_4 As Double, SupLowestTotal_5 As Double

Set r1 = Range(getSupplier_1)
Set r2 = Range(getSupplier_2)

With r1
   iColumnNo = .Column ' get the number representing the column of the cell.
   iRowNo = .Row    ' Get the first row of the range of cells
   iRowCount = .Rows.Count ' Get the number of rows for the range
End With

Supplier1ColumnNo = r1.Column ' Get the numeric name of the column.
Supplier2ColumnNo = r2.Column ' Get the numeric name of the column.

Supplier1ColumnName = GetCol(Supplier1ColumnNo) ' get the alpha name of the column
Supplier2ColumnName = GetCol(Supplier2ColumnNo) ' get the alpha name of the column

' start building the string to list the column names by starting with the first two mandatory suppliers (column names) before going to optional argument (i.e 3-5 suppliers)
strColumnList = r1.Column
strColumnList = strColumnList & "," & r2.Column

 If IsMissing(getSupplier_3) = False Then ' Check to see the there is a 3rd Supplier data which is passed to the funciton.
    Set r3 = Range(getSupplier_3)
    strColumnList = strColumnList & "," & r3.Column
    Supplier3ColumnNo = r3.Column ' Get the alphabetical name of the column.
    Supplier3ColumnName = GetCol(Supplier3ColumnNo) ' get the alpha name of the column
 End If
 
 If IsMissing(getSupplier_4) = False Then ' Check to see the there is a 4th Supplier data which is passed to the funciton.
    Set r4 = Range(getSupplier_4)
    strColumnList = strColumnList & "," & r4.Column
    Supplier4ColumnNo = r4.Column ' Get the alphabetical name of the column.
    Supplier4ColumnName = GetCol(Supplier4ColumnNo) ' get the alpha name of the column
 End If
 
 If IsMissing(getSupplier_5) = False Then ' Check to see the there is a 5th Supplier data which is passed to the funciton.
    Set r5 = Range(getSupplier_5)
    strColumnList = strColumnList & "," & r5.Column
    Supplier5ColumnNo = r5.Column ' Get the numeric name of the column.
    Supplier5ColumnName = GetCol(Supplier5ColumnNo) ' get the alpha name of the column
 End If
 
arrColNam() = Split(strColumnList, ",") ' pass the column number to an array to loop through later.

ReDim arrHolding(0 To UBound(arrColNam())) ' build the size of the array which would hold the values of the cells as we loop horizontally.

    For j = iRowNo To (iRowCount + iRowNo) - 1 'loop down the rows of the Supplier ranges.Basically we looping across and down.
        For i = LBound(arrColNam) To UBound(arrColNam)  ' loop through the array and output the column names.
            iCellValue = Cells(j, CDbl(arrColNam(i))).Value
             'Debug.Print j & " : " & iCellValue 'print the row number can cell value horizontally this was just for testing purposes
                        
            arrHolding(i) = iCellValue ' as we loop through horizonally pass the values in each cell to the holding array.
            colListValue = colListValue & "," & Str(iCellValue) ' this is just used for testing purposes to show all the values in a row once extracted.
                    
        Next i
        
        iLowestCellValue = BubbleSrt(arrHolding, True) ' use the function bubblesrt to sort the array and place in a variable to hold.
            
        c = 0
        
        Do Until iLowestCellValue(c) > 0 'Check to make sure the lowest value is not zero. if it is then check the next sequence in the array until a non zero value is found.
            c = c + 1
            iLowestCellValue(0) = iLowestCellValue(c) 'pass the next vaailable value that is greater than 0 to the array.
        Loop
            
        ' loop back thought the row and see which cell in the row matches the lowest value.
         
        For i = LBound(arrColNam) To UBound(arrColNam)  ' loop through the array and this time we are looking to see which cell matches the lowest data value.
            iCellValue2 = Cells(j, CDbl(arrColNam(i))).Value
                     
            If (iLowestCellValue(0) = iCellValue2) Then ' look for a match in values and change the color of the cell to yellow
                cellAddress = Cells(j, CDbl(arrColNam(i))).Address 'get the address of the cell which matches the lowest value
                alterCellColor (cellAddress) ' call the function which adjusts the color of a value which is passed to it.
            
                iarrColumName = Cells(j, CDbl(arrColNam(i))).Column ' match the names which are passed to the column array with the actual excel column and store the lowest value.
            
                If Supplier1ColumnNo = iarrColumName Then 'keep adding the lowest value for each supplier as we loop
                    SupLowestTotal_1 = SupLowestTotal_1 + iCellValue2
                ElseIf Supplier2ColumnNo = iarrColumName Then
                    SupLowestTotal_2 = SupLowestTotal_2 + iCellValue2
                ElseIf Supplier3ColumnNo = iarrColumName Then
                    SupLowestTotal_3 = SupLowestTotal_3 + iCellValue2
                ElseIf Supplier4ColumnNo = iarrColumName Then
                    SupLowestTotal_4 = SupLowestTotal_4 + iCellValue2
                ElseIf Supplier5ColumnNo = iarrColumName Then
                    SupLowestTotal_5 = SupLowestTotal_5 + iCellValue2
                End If
                     
            End If
    
        Next i
        
        Debug.Print j & ":- "; Mid(colListValue, 2) & ": in this row the lowest value is " & iLowestCellValue(0) & " in cell - " & cellAddress ' this is for testing purposes only. It shows the extracted row of values and the lowest value in the row
        colListValue = "" ' reset the column list.
        
    Next j
    
    outputMsg = "Supplier 1 in column '" & Supplier1ColumnName & "' total in yellow is = " & SupLowestTotal_1
    outputMsg = outputMsg & vbNewLine & "Supplier 2 in column '" & Supplier2ColumnName & "' total in yellow is = " & SupLowestTotal_2
    
    If Supplier3ColumnNo <> 0 Then ' build the output string once the parameters are passed (supplier data)
        outputMsg = outputMsg & vbNewLine & "Supplier 3 in column '" & Supplier3ColumnName & "' total in yellow is = " & SupLowestTotal_3
    End If
    
    If Supplier4ColumnNo <> 0 Then ' build the output string once the parameters are passed (supplier data)
        outputMsg = outputMsg & vbNewLine & "Supplier 4 in column '" & Supplier4ColumnName & "' total in yellow is = " & SupLowestTotal_4
    End If
    
    If Supplier5ColumnNo <> 0 Then ' build the output string once the parameters are passed (supplier data)
        outputMsg = outputMsg & vbNewLine & "Supplier 5 in column '" & Supplier5ColumnName & "' total in yellow is = " & SupLowestTotal_5
    End If
  
udf_IdentifyLowestCost = outputMsg

End Function

Private Function alterCellColor(cellName As String)

'code built from macro which formats the cells.
    Range(cellName).Select
    With Selection.Interior
        .Pattern = xlSolid
        .PatternColorIndex = xlAutomatic
        .Color = 65535
        .TintAndShade = 0
        .PatternTintAndShade = 0
    End With

End Function


Private Function BubbleSrt(ArrayIn, Ascending As Boolean) As Variant

' https://www.mrexcel.com/board/threads/vba-to-sort-an-array-of-numbers.690718/
' This code is used to sort an array of numbers.

Dim SrtTemp As Variant
Dim i As Long
Dim j As Long


If Ascending = True Then
    For i = LBound(ArrayIn) To UBound(ArrayIn)
         For j = i + 1 To UBound(ArrayIn)
             If ArrayIn(i) > ArrayIn(j) Then
                 SrtTemp = ArrayIn(j)
                 ArrayIn(j) = ArrayIn(i)
                 ArrayIn(i) = SrtTemp
             End If
         Next j
     Next i
Else
    For i = LBound(ArrayIn) To UBound(ArrayIn)
         For j = i + 1 To UBound(ArrayIn)
             If ArrayIn(i) < ArrayIn(j) Then
                 SrtTemp = ArrayIn(j)
                 ArrayIn(j) = ArrayIn(i)
                 ArrayIn(i) = SrtTemp
             End If
         Next j
     Next i
End If

BubbleSrt = ArrayIn

End Function

Private Function GetCol(ColumnNumber) As String

'passed a column number and returns the column name
'https://www.vitoshacademy.com/vba-calculating-column-name-from-number-in-excel-function/

    Dim FuncRange             As String
    Dim FuncColLength         As Integer
 
    FuncRange = Cells(1, ColumnNumber).AddressLocal(False, False)
    FuncColLength = Len(FuncRange)
    GetCol = Left(FuncRange, FuncColLength - 1)
 
End Function
 
Upvote 0
The alterCellColor sub routine is why it is failing as a worksheet function. UDF's called from the worksheet cannot change the sheet by coloring cells, if a UDF tries, an error value will be returned.
 
Upvote 0
OK, thanks for that, the problem is that a function called from the sheet cannot change the colour of a cell.
Nor can it alter a cell other than the one it's in.
 
Upvote 0
This is interesting and I didn't know that.

Ok, how can I then adjust the values on the cells to be distinctive so the user can see the lowest values.??

I spent time coding this only to not have it not work due to some Excel Design limitation (at least that is what I call it). Any thought and ideas are welcomed!!
 
Upvote 0
I am just sitting here thinking this is very silly. We can same a workbook as macro enable but don't have the full power to work within the workbook as we would want...
 
Upvote 0
Because you are using the function as a formula, it's limited to what a normal Excel function can do.

I would think the best way would be to use conditional formatting
 
Upvote 0
I found this link which explains more about the limitations of User Defined Functions . I am Access Programmer so working in excel over the last two days to code this and now finding out the limitations of UDF's was a shock. Hope someone reads this and does not fall into the same situation as I.

Thanks for all the help Fluff and Mike.
 
Upvote 0

Forum statistics

Threads
1,215,945
Messages
6,127,856
Members
449,411
Latest member
adunn_23

We've detected that you are using an adblocker.

We have a great community of people providing Excel help here, but the hosting costs are enormous. You can help keep this site running by allowing ads on MrExcel.com.
Allow Ads at MrExcel

Which adblocker are you using?

Disable AdBlock

Follow these easy steps to disable AdBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the icon in the browser’s toolbar.
2)Click on the "Pause on this site" option.
Go back

Disable AdBlock Plus

Follow these easy steps to disable AdBlock Plus

1)Click on the icon in the browser’s toolbar.
2)Click on the toggle to disable it for "mrexcel.com".
Go back

Disable uBlock Origin

Follow these easy steps to disable uBlock Origin

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back

Disable uBlock

Follow these easy steps to disable uBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back
Back
Top