From any cell in row 6, Name Manager > New, SandH Refers to: =$D6
Then proceed as before.
Shg,
This tip has saved me more time and frustration than I can imagine. Thank you so much.
Before you gave me this, I was using all kinds of convoluted and error-prone methods to pass a simple dynamically variable range to a UDF. Now, with one simple definition, everything works and it is easy to understand.
Here's a simple version of my latest implementation. The WtdRtg function calculates a weighted rating for products. Each product is given a rating (0-10) on each of several features. The features are then given relative weights.
In this table, we compare three products (A, B, & C) on three features (Speed, Waste, & Quiet). In this example, they are all given equal weight (5). Since their ratings all add up to the same value (20), they all get the same weighted average (6.6667).
[TABLE="class: grid, width: 800"]
<tbody>[TR]
[TD="align: center"]
R/C
[/TD]
[TD="align: center"]
C[/TD]
[TD="align: center"]
D[/TD]
[TD="align: center"]
E[/TD]
[TD="align: center"]
F[/TD]
[TD="align: center"]
G[/TD]
[TD="align: center"]
H[/TD]
[TD="align: center"]
I[/TD]
[TD="align: center"]
J[/TD]
[/TR]
[TR]
[TD="align: center"]
5[/TD]
[TD="align: right"]
Weights[/TD]
[TD="align: center"]
5[/TD]
[TD="align: center"]
5[/TD]
[TD="align: center"]
5[/TD]
[TD="align: center"]
Weighted[/TD]
[TD]
[/TD]
[TD="align: center"]
Weighted[/TD]
[TD]
[/TD]
[/TR]
[TR]
[TD="align: center"]
6[/TD]
[TD="align: right"]
Features[/TD]
[TD="align: center"]
Speed[/TD]
[TD="align: center"]
Waste[/TD]
[TD="align: center"]
Quiet[/TD]
[TD="align: center"]
Rating[/TD]
[TD="align: center"]
Formula[/TD]
[TD="align: center"]
Rating[/TD]
[TD="align: center"]
Formula[/TD]
[/TR]
[TR]
[TD="align: center"]
7[/TD]
[TD="align: right"]
Prod A[/TD]
[TD="align: center"]
3[/TD]
[TD="align: center"]
8[/TD]
[TD="align: center"]
9[/TD]
[TD="align: center"]
6.6667[/TD]
[TD]
G7: =wtdrtg(Ratings,Weights)[/TD]
[TD="align: center"]
6.6667[/TD]
[TD]
I7: =wtdrtg(C7:G7, C$5:G$5)[/TD]
[/TR]
[TR]
[TD="align: center"]
8[/TD]
[TD="align: right"]
Prod B[/TD]
[TD="align: center"]
6[/TD]
[TD="align: center"]
8[/TD]
[TD="align: center"]
6[/TD]
[TD="align: center"]
6.6667[/TD]
[TD]
G8: =wtdrtg(Ratings,Weights)[/TD]
[TD="align: center"]
6.6667[/TD]
[TD]
I8: =wtdrtg(C8:G8, C$5:G$5)[/TD]
[/TR]
[TR]
[TD="align: center"]
9[/TD]
[TD="align: right"]
Prod C[/TD]
[TD="align: center"]
8[/TD]
[TD="align: center"]
7[/TD]
[TD="align: center"]
5[/TD]
[TD="align: center"]
6.6667[/TD]
[TD]
G9: =wtdrtg(Ratings,Weights)[/TD]
[TD="align: center"]
6.6667[/TD]
[TD]
I9: =wtdrtg(C9:G9, C$5:G$5)[/TD]
[/TR]
</tbody>[/TABLE]
The formula in G as shown in H, uses the named ranges as you suggested.
- Ratings is defined as [$Cr:$Gr]. The columns are static. The rows are dynamic. They will assume the value of the row where it is referenced. So the call in G8 will pass [$C$8:$G$8].
- Weights is defined as [$C5:$G$5]. Both the rows and columns are static, so will remain the same regardless of where they are called from.
The formula in I as shows in J passes the same ranges, but is less readable.
Both ranges include the columns before and after the data columns (D:F). This allows me to add columns for new features without having to redefine the ranges. The function knows this and will process cells 2 through N-1. (See code below.)
Now suppose we care more about Speed and less about Quiet. We can change the weights and get different weighted averages.
[TABLE="class: grid, width: 600"]
<tbody>[TR]
[TD="align: center"]R/C[/TD]
[TD="align: center"]C[/TD]
[TD="align: center"]D[/TD]
[TD="align: center"]E[/TD]
[TD="align: center"]F[/TD]
[TD="align: center"]G[/TD]
[TD="align: center"]H[/TD]
[/TR]
[TR]
[TD="align: center"]5[/TD]
[TD="align: center"]Weights[/TD]
[TD="align: center"]10[/TD]
[TD="align: center"]5[/TD]
[TD="align: center"]1[/TD]
[TD="align: center"]Weighted[/TD]
[TD="align: center"][/TD]
[/TR]
[TR]
[TD="align: center"]6[/TD]
[TD="align: right"]Features[/TD]
[TD="align: center"]Speed[/TD]
[TD="align: center"]Waste[/TD]
[TD="align: center"]Quiet[/TD]
[TD="align: center"]Rating[/TD]
[TD="align: center"]Formula[/TD]
[/TR]
[TR]
[TD="align: center"]7[/TD]
[TD="align: right"]Prod A[/TD]
[TD="align: center"]3[/TD]
[TD="align: center"]8[/TD]
[TD="align: center"]9[/TD]
[TD="align: center"]4.9375[/TD]
[TD]G7: =wtdrtg(Ratings,Weights)[/TD]
[/TR]
[TR]
[TD="align: center"]8[/TD]
[TD="align: right"]Prod B[/TD]
[TD="align: center"]6[/TD]
[TD="align: center"]8[/TD]
[TD="align: center"]6[/TD]
[TD="align: center"]6.6250[/TD]
[TD]G8: =wtdrtg(Ratings,Weights)[/TD]
[/TR]
[TR]
[TD="align: center"]9[/TD]
[TD="align: right"]Prod C[/TD]
[TD="align: center"]8[/TD]
[TD="align: center"]7[/TD]
[TD="align: center"]5[/TD]
[TD="align: center"]7.5000[/TD]
[TD]G9: =wtdrtg(Ratings,Weights)[/TD]
[/TR]
</tbody>[/TABLE]
Now product C is the clear winner.
Here's the code:
Code:
'===============================================================================================
' Test Variable Range
' This function tests two ranges, Ratings & Weights.
' The range is defined to include the column before and the column after the target range.
' See the workbook for details.
' Change Log
' 04/16/19 Created based on information from shg on Mr. Excel, thread [URL=https://www.mrexcel.com/forum/usertag.php?do=list&action=hash&hash=1086964]#1086964[/URL] .
'===============================================================================================
Public Function WtdRtg(Ratings As Range, Weights As Range) As Double
Dim Col1 As Long 'The first row
Dim Coln As Long 'The last row
Dim NumCols As Long 'The number
Dim iCol As Integer 'Column loop index
Dim NextRtg As Double 'Temp variable for next rating in range
Dim NextWt As Double 'Temp variable for next weight in range
Dim SumWtdRtgs As Double 'Tally the weighted ratings
Dim SumWts As Double 'Tally the weights
NumCols = Ratings.Columns.Count 'The number of columns
Col1 = 2 'Start with the second column
Coln = NumCols - 1 'End with the next to last column
SumWtdRtgs = 0 'Initialize the sum of the weighted ratings
SumWts = 0 'Initialize the sum of the weights
For iCol = Col1 To Coln 'Loop through columns
NextRtg = Ratings(1, iCol) 'Get the next rating
NextWt = Weights(1, iCol) 'Get the next weight
SumWtdRtgs = SumWtdRtgs + (NextRtg * NextWt)
SumWts = SumWts + NextWt
Next iCol
If SumWts = 0 Then 'If the sum of the weights = 0
WtdRtg = 0 'Avoid divide by zero
Else 'If OK,
WtdRtg = SumWtdRtgs / SumWts 'Calculate the weighted rating
End If
End Function
Again, thank you Shg. This has made my life much easier.
PS: I would welcome any comments or suggestions for improving this code.