Reputation: 23
I'm trying to write a UDF (user-defined function) to create an average for non-numeric data (I'm converting it into numeric form then back again at the end). I can get the UDF to work if I list individual cells; I get a #VALUE! error if I try to refer to a range of cells. There may be a mix of both ranges and individual cells to process.
Any ideas?
The code so far is below.
Function avlvl(ParamArray av() As Variant)
Dim a As Integer
'creates an average ks3 level from data in format "5a"
a = 0
n = 0
total = 0
Do While a < UBound(av()) + 1
'ignore blank or zero cells
If av(a) = 0 Or av(a) = "" Then
a = a + 1
Else
'convert data into numeric value - split into level and sub level
level = Val(Left(av(a), 1))
sl = Right(av(a), 1)
If sl = "c" Then
sublevel = 0
ElseIf sl = "C" Then
sublevel = 0
ElseIf sl = "b" Or sl = "B" Then
sublevel = 1 / 3
ElseIf sl = "a" Or sl = "A" Then
sublevel = 2 / 3
Else
sublevel = 0
End If
'score is numeric value of the data
score = level + sublevel
'total is teh toatl of the cells so far
total = total + score
a = a + 1
n = n + 1
End If
Loop
ave = total / n
'reconvert into format level and sublevel (a,b,c)
averagelevel = Application.WorksheetFunction.RoundDown(ave, 0)
asl = ave - averagelevel
If asl < 0.17 Then
averagesublevel = "c"
ElseIf asl < 0.5 Then
averagesublevel = "b"
ElseIf asl < 0.84 Then
averagesublevel = "a"
ElseIf asl < 1 Then
averagelevel = averagelevel + 1
averagesublevel = "c"
Else
averagesublevel = "c"
End If
avlvl = averagelevel & averagesublevel
End Function
Upvotes: 2
Views: 2367
Reputation: 5866
What's going on is that the range is coming in as a single object of type Range, and your code is trying to treat is as though it is coming in as an array.
The best approach would be to create a new array within the body of the function, and then assign the elements in the range to the new array. You need to test for the type of the elements of the ParamArray. If an element is type String, then put it directly in the new array; if an element is type Range, loop through it, assigning its cell values to the new array.
Then you would do your processing on the new array.
The following code provides the machinery to pass in ranges as well as individual cells or values. I've not included your code but have indicated where it would go.
Function avlvl(ParamArray av() As Variant) As Variant
Dim a As Integer
Dim i As Long
Dim avArr()
Dim element As Variant
a = 0
i = 0
Do While a < UBound(av) + 1
If TypeName(av(a)) = "String" Then
avArr(i) = av(a)
i = i + 1
ElseIf TypeName(av(a)) = "Range" Then
For Each element In av(a)
ReDim Preserve avArr(0 To i)
avArr(i) = element
i = i + 1
Next
Else
avlvl = CVErr(xlErrValue)
Exit Function
End If
a = a + 1
Loop
i = 0
Do While i < UBound(avArr) + 1
'...
'now process the elements of avArr()
'...
i = i + 1
Loop
End Function
Upvotes: 2
Reputation: 96753
If you have a disjoint range of cells and you want to pass them to a UDF, one approach is to create a Defined Name and pass it to the UDF as a single argument.
Upvotes: 0