Type Mismatch in GetValue function when using Strings as inputs

Viewed 87

I'm trying to make my function look for a value in a table from another sheet. The rows of the table are labelled with numbers but the columns are named with strings.

Here is my function code

Function GetValue(row As String, col As String)
    Dim ws As Worksheet
    Dim ValueSearch As Range

    Set ws = Sheet2
    Set ValueSearch = ws.Range("Wheels")

    GetValue = ValueSearch.Cells(row, col)
End Function

My Table is on sheet 2 and is named Wheels

WheelSize NoTyre Budget Premium
    16      150    200   240
    17      180    220   265
    18      215    245   310
    19      255    360   460
    20      340    550   600

I have 2 comboboxes with the WheelSize number and the tyre type and am looking to use these to cross-reference to end up with the price

5 Answers

I transformed your data into a table.
Here is the code that will solve your problem:

Public Function GetValue(row As String, col As String)
    GetValue = Range("Wheels").Cells(Range("Wheels[WheelSize]").Find(row).row - 1, Range("Wheels[#Headers]").Find(col).Column)
End Function

I do not know why you would want to use a VBA for this task. You can use this formula:

=INDEX(Wheels,MATCH(G2,Wheels[WheelSize],0),MATCH(G3,Wheels[#Headers],0))

where G2 is the wheel size and G3 is the tire type.

In the attachment you can see the VBA solution and the formula as well.
https://gofile.io/d/nnT7nc
Write a comment if you need more clarifications.

Function GetValue(tmProw&, tmPcol&)
    Arr = Application.Names("Wheels").RefersToRange.Value
    GetValue = Arr(tmProw + 1, tmPcol)
End Function

enter image description here

ps: Row + 1

Based on your comments, I would accomplish this using Form Controls and formulas:

  1. Make a transpose of your TyreType array in a hidden location.
  2. Set up your Combo Box Form Controls > Format Control > Cell Link to a cell behind themselves.
  3. Set up your Combo Box Form Controls > Format Control > Input Range to the header range or column range, depending on either TyreType or WheelSize.
  4. Result cell should be the formula below, where Wheels is only the output data, not including headers. If you include the headers in Wheels, then add one the row and column numbers.

=Index(Wheels, WheelSize Cell link range, TyreType Cell link range)

Example: =INDEX(B2:C4,F2,F4) or =INDEX(Wheels, F2 + 1, F4 + 1)

enter image description here

A For...Next Loop Solution

Option Explicit

Enum TyreType
    ' Range (table) column numbers, not necessarily worksheet column numbers.
    Budget = 3
    Premium = 4
End Enum

Function getPrice( _
    ByVal Wheel As Long, _
    ByVal Tyre As Long, _
    Optional ByVal PriceType As TyreType = Budget) _
As Double
    Dim Data As Variant: Data = Sheet2.Range("Wheels").Value
    Dim i As Long
    For i = 2 To UBound(Data, 1)
        If Data(i, 1) = Wheel And Data(i, 2) = Tyre Then
            getPrice = Data(i, PriceType)
            Exit For
        End If
    Next i
End Function

Sub getPriceTEST()
    Debug.Print getPrice(18, 215, Premium) ' 310
    Debug.Print getPrice(19, 255, Budget) ' 360
    Debug.Print getPrice(18, 255, Budget) ' 0
    Debug.Print getPrice(17, 180) ' 220
End Sub

Easy one, with named ranges. You have to do:

  1. Select all data
  2. Create named ranges using FORMULAS->Create from selection
  3. In the Dialogbox, make sure you choose Top row

enter image description here

This will create instantly 4 named ranges, and each name will be the top row (WheelSize,NoTyre, Budget, Premium)

Then your UDF can be just a SUMIF

Function GetValue(vWheelSize As String, VTyreType As String)
GetValue = Application.WorksheetFunction.SumIf(Sheet2.Range("WheelSize"), vWheelSize, Sheet2.Range(VTyreType))
End Function

Because you are getting a number, you can use SUMIF instead of VLOOKUP or INDEX or MATCH. You want to sum up the tyre type range chosen, and your criteria is the wheelsize.

Notice I forced the UDF to be volatile to make sure it takes any change, you if you use this UDF at the same time in thousands of cells, it may overcharge your file.

But in your form and taking values from 2 comboboxes, it should not be a problem at all.

OPTION 2:

This is what I recomend, indeed. I don't know if your data is just a range or a table (a ListObject)

I would indeed use a ListObject so if you add new data, those new values are autoincluded. With named ranges from first solution, you need to manually update the size of the range.

Let's say you create a Table named WHEELS with four columns with headers WheelSize,NoTyre, Budget, and PRemium. In this case, you don't need to create the named ranges. You just need this code for your UDF:

enter image description here

Function GetValue(vWheelSize As String, VTyreType As String)
Application.Volatile True
GetValue = Application.WorksheetFunction.SumIf(Range("WHEELS[WheelSize]"), vWheelSize, Range("WHEELS[" & VTyreType & "]"))
End Function
Related