Related
I have two tables in Excel, one with categories and listings, and another with points based on the category and listing threshold. It goes as follows:
Categories table:
ID
CATEGORY
LISTINGS
Points
001
A
56
002
C
120
003
A
4
004
B
98
Points table:
Category
tier1
tier2
tier3
A
Tier 1
Tier 2
Tier 3
Range
1-30
31-90
91-
Points
10
20
30
B
Tier 1
Tier 2
Tier 3
Range
1-25
26-100
101-
Points
10
20
30
C
Tier 1
Tier 2
Tier 3
Range
1-40
41-80
81-
Points
10
20
30
I started with an INDEX MATCH formula pointing at the points:
=INDEX(Points!A1:D11, MATCH(Categories!B2, Points!A1:A11, 0)+2)
--> the +2 is to get the points directly
I also though of evaluating the thresholds with this formula:
=IF(Categories!C2 >= NUMBERVALUE(LEFT(Points!D3, FIND("-",Points!D3)-1)),Points!D4, IF(Categories!C2 >=NUMBERVALUE(LEFT(Points!C3, FIND("-",Points!C3)-1)),Points!C4, Points!B4))
I thought that the else if the if would make it faster.
Could someone help me populate the Points column in the Categories table? VBA code is also acceptable. The tables are in different sheets.
José, your original Match formula is the right starting place. It locates the particular sub-table to do a further lookup on. But in order to make this formula a whole lot simpler, can we change the ranges (1-30, 31-90, 91-) to have just their starting points (1, 31, 91)? If we do that simple change then we can use the approximate lookup feature of HLookup to easily and compactly specify the Lookup:
=HLookup($C2,Offset(Points!$A$1,Match($B2,Points!$A:$A,0),1,2,3),2,True)
In the middle of this formula you can see your original Match function to locate the correct sub-table based on the category. We need to feed that start point to OFFSET() to create a table range useful to the HLookup. Offset takes an anchor cell reference (top left of the Points table), number of rows to count down from there (result of the Match), number of columns to the right (1), the number of rows in the range (2), and the number of columns in the range (3).
The Hlookup is just like a VLookup, but for tables arranged left-to-right not top-to-bottom. The True as the last parameter is very important as it tells HLookup to use the range lookup instead of an exact match.
A slightly verbose formula which takes the data as originally formatted (using Excel 365 Let):
=LET(ranges,INDEX(Points!B$2:D$12,MATCH(B2,Points!A$1:A$12,0),0),
leftRanges,VALUE(LEFT(ranges,FIND("-",ranges)-1)),
points,INDEX(Points!B$2:D$12,MATCH(B2,Points!A$1:A$12,0)+1,0),
INDEX(points,MATCH(C2,leftRanges)))
As it's urgent this is what I got but I cannot continue before tomorrow. Assuming you can at least put the tiers next to the category (A on same line as tiers) this will get you the first tier. You just need to copy the whole formula in the "nextCol", with minor modifications to get the next tiers.
if you really cannot change the source you need to add an additional offset in the first match.
=IF(AND(D1>NUMBERVALUE(LEFT(INDIRECT("B"&MATCH(E1,A:A,0)),FIND("-",INDIRECT("B"&MATCH(E1,A:A,0)))-1)),D1<NUMBERVALUE(RIGHT(INDIRECT("B"&MATCH(E1,A:A,0)),2))),INDEX(B:D,MATCH(E1,A:A,0)+1,1),"nextCol")
Option Explicit
Sub Score()
Dim wsP As Worksheet, wsC As Worksheet, dict
Dim iLastRow As Long, r As Long, i As Long, j As Integer
Dim sCat As String, iListing As Integer
Dim data, ar
Set wsP = Sheets("Points")
Set wsC = Sheets("Categories")
Set dict = CreateObject("Scripting.Dictionary")
iLastRow = wsC.Cells(Rows.Count, 1).End(xlUp).Row
data = wsC.Range("A1:D" & iLastRow).Value2
' category to row lookup
For i = 1 To UBound(data)
If data(i, 1) = "Range" Then
dict.Add Trim(data(i - 1, 1)), i ' range
End If
Next
' scan points
iLastRow = wsP.Cells(Rows.Count, 1).End(xlUp).Row
For r = 2 To iLastRow
sCat = Trim(wsP.Cells(r, "B"))
iListing = wsP.Cells(r, "C")
If dict.exists(sCat) Then
i = dict(sCat)
For j = 4 To 2 Step -1
ar = Split(data(i, j), "-") ' lower-upper limit
If iListing >= ar(0) Then ' check lower limit
' update points
wsP.Cells(r, "D") = data(i + 1, j) ' points
Exit For
End If
Next
Else
MsgBox "No match '" & sCat & "'", vbCritical, "ERROR row " & r
End If
Next
MsgBox "done"
End Sub
Please, try the next code. It uses arrays and should be very fast, working only in memory. Please use your sheets when setting shC and shP as your real sheets. I only use the active sheet and the next one for testing reason:
Sub GetPoints()
Dim shC As Worksheet, shP As Worksheet, lastRC As Long, lastRP As Long, arrBC, arrP, arrPP, arrFin
Dim i As Long, j As Long, p As Long, k As Long
Set shC = ActiveSheet 'use here your Categories sheet
Set shP = shC.Next 'use here your Points sheet
lastRC = shC.Range("A" & shC.rows.count).End(xlUp).row
lastRP = shP.Range("A" & shP.rows.count).End(xlUp).row
arrBC = shC.Range("B2:C" & lastRC).Value 'put the range B:C in an array
arrP = shP.Range("A2:D" & lastRP).Value 'put all the range in an array
ReDim arrFin(1 To UBound(arrBC), 1 To 1) 'redim the array to keep processed values
For i = 1 To UBound(arrBC) 'iterate between Categ array elements:
For j = 1 To UBound(arrP) 'iterate between Points array elements:
If arrP(j, 1) = arrBC(i, 1) Then 'if Category is found:
For p = 2 To 4 'iterate between the next array row elements
arrPP = Split(arrP(j + 1, p), "-") 'split the element by "-" to determine the interval
If arrPP(1) <> "" Then 'for the tier3 case:
If arrBC(i, 2) >= CLng(arrPP(0)) And arrBC(i, 2) <= CLng(arrPP(1)) Then
k = k + 1
arrFin(k, 1) = arrP(j + 2, p): j = j + 2: Exit For 'place the value in the final array and exit iteration
End If
Else 'for the tier1 and tier2 cases:
If arrBC(i, 2) >= CLng(arrPP(0)) Then
k = k + 1
arrFin(k, 1) = arrP(j + 2, p): j = j + 2: Exit For 'place the value in the final array and exit iteration
End If
End If
Next p
End If
Next j
Next i
'drop the final array result at once:
shC.Range("D2").Resize(UBound(arrFin), 1).Value = arrFin
End Sub
I have been battling with this code for several days now and would appreciate some guidance on where I am going wrong.
My project is to create a printable document format containing manufacturing instructions and spaces for operators to write manual entries, which requires a minimum cell size when printed onto A4. These instructions will be varied but in all cases will be signed in column B, and in some cases will be countersigned. The marker in column B for a signature is "Op" and the marker for a countersignature is "Check".
In order to regulate cell size for the printed document I am attempting to count row heights up until a fixed total (832), from that point I want the code to go up and look for the first "Op", if the "Op" has a "Check" in the cell below then insert a page break below "Check", if not then insert a page break below "Op". From there I want the code to continue to the bottom of the document inserting page breaks every time it counts 832 total rows.
I am not sure if that methodology is the best for achieving what I am aiming for but would appreciate some feedback on what I have so far, I am getting an run time error 1004 on this code and it is inserting page breaks in the wrong places.
Sub TotalHeight()
Dim HowTall As Long
Dim Count As Long
Dim TotalHeight As Long
HowTall = 0
Count = 0
TotalHeight = 0
Dim cell As Range
For Each cell In Columns("B").Cells.SpecialCells(xlCellTypeConstants)
'If Not cell.Hidden Then
HowTall = HowTall + cell.RowHeight
'Count = Count + 1
If HowTall > 832 Then
Debug.Print cell.Row
For tmpcounter = 0 To 100
' If (Range(cell.Row).Offset(-tmpcounter, 0).Value) = "Op" Then
If cell.Offset(-tmpcounter, 0).Value = "Op" Then
If cell.Offset(-tmpcounter + 1, 0).Value = "Check" Then
'Found Check - get current row
PageBreakRowNo = cell.Offset(-tmpcounter + 1, 0).Row
Debug.Print "Check found at row " & PageBreakRowNo
Sheets("Dispensary").HPageBreaks.Add Before:=cell.Offset(-tmpcounter + 2, 0)
Else
'Only found Op - Get current row
PageBreakRowNo = cell.Offset(-tmpcounter, 0).Row
Debug.Print "Op found at row " & PageBreakRowNo
Sheets("Dispensary").HPageBreaks.Add Before:=cell.Offset(-tmpcounter + 1, 0)
End If
End If
Next tmpcounter
End If ' end of of HowTall >832 loop
Next 'end of for each cell in Column B loop
End Sub
Added my suggested improvements in your original code:
Sub TotalHeight()
Dim HowTall As Long
Dim Count As Long
Dim TotalHeight As Long
Dim tmpcounter1 As Long
HowTall = 0
Count = 0
TotalHeight = 0
Dim cell As Range
For Each cell In Columns("B").Cells.SpecialCells(xlCellTypeConstants)
'If Not cell.Hidden Then
HowTall = HowTall + cell.RowHeight
'Count = Count + 1
If HowTall > 832 Then
Debug.Print cell.Row
For tmpcounter = 0 To 100
If cell.Offset(-tmpcounter, 0).Value = "Op" Or cell.Offset(-tmpcounter, 0).Value = "Check" Then
PageBreakRowNo = cell.Offset(-tmpcounter + 1, 0).Row
tmpcounter1 = tmpcounter
Debug.Print "Op / Check found at row " & PageBreakRowNo
Sheets("Dispensary").HPageBreaks.Add Before:=cell.Offset(-tmpcounter + 2, 0)
Exit for 'to stop looking upwards
End If
Next tmpcounter
HowTall = 0 'reset HowTall to start again for the next page
For i = tmpcounter1 +1 to 0 Step -1 'Calculate the HowTall lost due to stepping back
HowTall = HowTall + cell.Offset(i,0).RowHeight
Next i
End If ' end of of HowTall >832 loop
Next 'end of for each cell in Column B loop
End Sub
I need to find the average of a number found in column F if a particular value is found in any of the other columns in the sheet.
For instance: I have the following in a range...
A B C D E F
Red Bill Jack Ruby Bill 250
Blue Ruby Ivan Raul Ted 350
Green Ted James Rick Ted 125
Red Ted Phil Ruby Bill 300
And in this worksheet, I want to find any instance of the name Bill and get the average of the number found in column F. In this case, the answer of 275 because Bill's name shows up in two rows. In the same respect, If I choose to look at Ted's numbers, the answer should be 258 because Ted's name shows up in three rows.
I would also appreciate if the formula would ignore any blank cells in the process of calculating the answer.
Thanks in advance!
I would use the function below, assuming that the data is placed in Sheet1.
Function my_average(strName As String) As Variant
Dim varArrayNames As Variant
Dim varValues As Variant
Dim dblInSum(1 To 4) As Double '~~> change to "1 To 40"
Dim lngCnt As Long
Dim strRow As String
Dim dblSum As Double
varArrayNames = Sheet1.Range("B1:E4").Value '~~> change to "B1:G40"
varValues = Sheet1.Range("F1:F4").Value '~~> change to "H1:H40"
For lngCnt = LBound(varArrayNames, 1) To UBound(varArrayNames, 1)
strRow = Join(WorksheetFunction.Index(varArrayNames, lngCnt, 0))
If InStr(strRow, strName) > 0 Then
dblInSum(lngCnt) = 1
End If
Next lngCnt
dblSum = WorksheetFunction.Sum(dblInSum)
If dblSum > 0 Then
my_average = WorksheetFunction.SumProduct(dblInSum, Application.Transpose(varValues)) / dblSum
Else
my_average = 0
End If
End Function
Testing:
Place =my_average("Bill") in any workbook (or a cell reference instead of "Bill").
Formulas:
Results:
Assuming the lookup value (Bill etc) is in cell C7, add the following formula in G1 then copy down for other rows.
=IF(ISERROR(MATCH($C$7,A1:E1,0)),"",F1)
Then do
=AVERAGE(G1:G4)
So if Bill is in any col a-e that number is taking into the ave... If so depending on your data size why not do this simply...:
Sub simplesearch()
cnt = 0
tot = 0
srchval = InputBox("What are we looking for?")
lr = Range("A1000000").End(xlUp).Row
For i = 1 To lr
For j = 1 To 5
If Cells(i, j).Value = srchval Then
tot = tot + Cells(i, 6).Value
cnt = cnt + 1
End If
Next j
Next i
If Not (cnt = 0) Then
MsgBox (tot / cnt)
Else
MsgBox ("0")
End If
End Sub
I am having trouble determining a way to enter a 1 or 0 into an adjacent cell to indicate whether or not a value is unique when working with a large dataset. I have read of multiple methods for accomplishing this, however none of them seem efficient for my purposes: I am using an instance of Excel 2010 (so I do not have the Distinct Count feature in PivotTables, and when I try to use PowerPivot it crashes my file due to processing limitations.
In this StackOverflow question: Simple Pivot Table to Count Unique Values there are suggestions to use SUMPRODUCT or COUNTIF, but when working with 50,000+ rows as I am, this causes terrible performance and a file size of ~35 MB instead of ~3 MB. I wanted to know if there is a better solution for a large, dynamic dataset whether it is a formula or VBA.
An example of what I would like to accomplish is (with the Unique column being the adjacent cell):
Name Week Unique
John 1 1
Sally 1 1
John 1 0
Sally 2 1
I attempted to script the same functionality of COUNTIF but with no success:
For Each Cell In ThisWorkbook.Worksheets("Overtime & Type Data").Range("Z2:Z" & DataLastRow)
If Worksheets("Overtime & Type Data").Cells(Cell.Row, 26) <> Worksheets("Overtime & Type Data").Cells(Cell.Row - 1, 26) Then
FirstCell = Cell.Row
End If
If (Worksheets("Overtime & Type Data").Range(Cells(FirstCell, 26), Cells(Cell.Row, 26)) = Worksheets("Overtime & Type Data").Range(Cells(Cell.Row, 26))) = True Then
Cell.Value = 1
Else
Cell.Value = 0
End If
Next Cell
This code ran on over 130,000 rows successfully in less than 3 seconds. Adjust the column letters to fit your dataset.
Sub tgr()
Const colName As String = "A"
Const colWeek As String = "B"
Const colOutput As String = "C"
Dim ws As Worksheet
Dim rngData As Range
Dim DataCell As Range
Dim rngFound As Range
Dim collUniques As Collection
Dim arrResults() As Long
Dim ResultIndex As Long
Dim UnqCount As Long
Set ws = ThisWorkbook.Sheets("Overtime & Type Data")
Set rngData = ws.Range(colName & 2, ws.Cells(Rows.Count, colName).End(xlUp))
Set collUniques = New Collection
ReDim arrResults(1 To rngData.Cells.Count, 1 To 1)
On Error Resume Next
For Each DataCell In rngData.Cells
ResultIndex = ResultIndex + 1
collUniques.Add ws.Cells(DataCell.Row, colName).Value & ws.Cells(DataCell.Row, colWeek).Value, ws.Cells(DataCell.Row, colName).Value & ws.Cells(DataCell.Row, colWeek).Value
If collUniques.Count > UnqCount Then
UnqCount = collUniques.Count
arrResults(ResultIndex, 1) = 1
Else
arrResults(ResultIndex, 1) = 0
End If
Next DataCell
On Error GoTo 0
ws.Cells(rngData.Row, colOutput).Resize(rngData.Cells.Count).Value = arrResults
End Sub
One approach is to sort by Name and Week. Then you can determine Unique for any row by comparing with the previous row.
If you need to preserve the order, you could first write a column of Index numbers (1, 2, 3, ...) to keep track of order. After calculating Unique, sort by Index to restore the original order.
The whole process could be done manually with relatively few steps, or automated with VBA.
I'm not sure how well this will work with 50000 values, but it goes through ~1500 in about a second.
Sub unique()
Dim myColl As New Collection
Dim isDup As Boolean
Dim myValue As String
Dim r As Long
On Error GoTo DuplicateValue
For r = 1 To Sheet1.UsedRange.Rows.Count
isDup = False
'Combine the value of the 2 cells together
' and add that string to our collection
'If it is already in the collection it errors
myValue = Sheet1.Cells(r, 1).Value & Sheet1.Cells(r, 2).Value
myColl.Add r, myValue
If isDup Then
Sheet1.Cells(r, 3).Value = "0"
Else
Sheet1.Cells(r, 3).Value = "1"
End If
Next
On Error GoTo 0
Exit Sub
DuplicateValue:
'The value is already in the collection so put a 0
isDup = True
Resume Next
End Sub
Just about any bulk operation will beat a loop involving worksheet cells. You might be able to trim the time down a bit by performing all of the calculations in memory and only returning the values back to the worksheet en masse when it is complete.
Sub is_a_dupe()
Dim v As Long, vTMP As Variant, vUNQs As Variant, dUNQs As Object
Debug.Print Timer
On Error GoTo bm_Uh_Oh
Set dUNQs = CreateObject("Scripting.Dictionary")
With Worksheets("Sheet1")
vTMP = .Range(.Cells(2, 1), .Cells(Rows.Count, 2).End(xlUp)).Value2
ReDim vUNQs(1 To UBound(vTMP, 1), 1 To 1)
For v = LBound(vTMP, 1) To UBound(vTMP, 1)
If dUNQs.Exists(Join(Array(vTMP(v, 1), vTMP(v, 2)))) Then
vUNQs(v, 1) = 0
Else
dUNQs.Add Key:=Join(Array(vTMP(v, 1), vTMP(v, 2))), _
Item:=vTMP(v, 2)
vUNQs(v, 1) = 1
End If
Next v
.Cells(2, 3).Resize(UBound(vUNQs, 1), 1) = vUNQs
End With
Debug.Print Timer
bm_Uh_Oh:
dUNQs.RemoveAll
Set dUNQs = Nothing
End Sub
Previous experience tells me that the variety of data (as well as hardware, etc) will impact timing the process but in my random sample data I received these elapsed times.
50K records ..... 0.53 seconds
130K records .... 1.32 seconds
500K records .... 4.92 seconds
How do I have to change the following macro code and formula which works for the data and question below(earlier data/question,column F) to make it suitable for Problemstatement (2) and (3) ?
(1)
Earlier data
Colums C,D,E,F
Row 1 4,10,40,F
Row 2 4,12,48,F
Row 3 4,14,56,F
Row 4 3,16,48,F
Row 5 1,18,18,F
Row 6 1,20,10,F
Row 7 0,22,0,0
Intention of the column F
If Cx <> 0, Fx = Cx
If Cx = 0, Fx = the address of the cell in Column C that produces minimum of (C1 * D7 - E1, C2 * D7 - E2, ..., CN * D7 - EN) and is >0.
** Macro code and formula for column F**
Public Function MinimumC()
Dim rngCurrent As Range
Set rngCurrent = Application.ThisCell
Dim rngMin As Range
Dim minimum As Long
minimum = 100000000
Dim tmp As Long
Dim rngC As Range
Set rngC = ActiveSheet.Range("C1:C" & rngCurrent.Row - 1)
For Each c In rngC.Cells
If c.Value2 <> 0 Then
tmp = c.Value2 * rngCurrent.Offset(0, -2).Value2 - c.Offset(0, 2)
If tmp < minimum Then
minimum = tmp
Set rngMin = c
End If
End If
Next c
MinimumC = rngMin.Value2
End Function
Formula in F1 and copy down column F: =IF(C1<>0,C1,MinimumC())
(2)
How do I have to change the macro and formula to archieve the same in the following data format:
New data 1
Colums AZ,BA,BB,BC,BD,BE,BF,BG
Row 1 4,4,4,10,10,10,120,444
Row 2 4,4,4,12,12,12,144,444
Row 3 4,4,4,14,14,14,168,444
Row 4 3,3,3,16,16,16,144,333
Row 5 1,1,1,18,18,18,54,111
Row 6 1,1,1,20,20,20,60,111
Row 7 0,0,0,22,22,22,0,???
Intention of the column BG
If(And(AZ>0;BA>0;BB>0);Concatenate(AZ;BA;BB))
otherwise, the adress cell of Concatenate (AZ;BA;BB), unequal to 000, to minimize the following difference (AZn*BCx+BAn*BDx+BBn*BEx)-BFn
(3)
How do I have to change the macro and formula from (1) to archieve the same in the following data format:
New data 2
Colums AZ,BA,BB,BC,BD,BE,BF,BG,BH,BI,BJ,BK,BL,BM,BN
Row 1 4,4,4,10,10,10,120,444,3,3,3,10,10,10,90,333
Row 2 4,4,4,12,12,12,144,444,3,3,3,12,12,12,108,333
Row 3 4,4,4,14,14,14,168,444,3,3,3,14,14,14,126,333
Row 4 3,3,3,16,16,16,144,333,3,3,3,16,16,16,144,333
Row 5 1,1,1,18,18,18,54,111,2,2,2,18,18,18,108,222
Row 6 1,1,1,20,20,20,60,111,1,1,1,20,20,20,60,111
Row 7 0,0,0,22,22,22,0,?,0,0,0,22,22,22,0,?
Intention of the column BN
If(And(AZ>0;BA>0;BB>0;BH>0;BI>0;BJ>0);0
otherwise the adress of the cell either concatenate (AZ;BA;BB) or concatenate (BH;BI;BJ) to minimize (AZn*BCx+BAn*BDx+BBn*BEx)-BFn or (BHn*BKx+BIn*BLx+BJn*BMx)-BNn .In this case i need to find the adress of either concatenate (AZ;BA;BB) or concatenate (BH;BI;BJ) with the min difference and that cell of BN (concatenate) unequal to 000.
I appriciate the help. Thank you very much!!!!
To solve problem 2, the macro is basically identical. You just need to replace the C range with the AZ range, and change the calculation of tmp:
Public Function CalcBG()
Dim rngCurrent As Range
Set rngCurrent = Application.ThisCell
Debug.Print rngCurrent.Address
Dim rngMin As Range
Dim minimum As Long
minimum = 100000000
Dim tmp As Long
Dim rngAZ As Range
Set rngAZ = ActiveSheet.Range("AZ1:AZ" & rngCurrent.Row - 1)
Debug.Print rngAZ.Address
For Each c In rngAZ.Cells
If c.Value2 <> 0 Then
tmp = ((c.Value * rngCurrent.Offset(0, -4).Value2) + (c.Offset(0, 1).Value2 * rngCurrent.Offset(0, -3).Value2) + (c.Offset(0, 2).Value2 * rngCurrent.Offset(0, -2)) - c.Offset(0, 6).Value2) 'This is your calculation (AZn * BCx) + ... - BFn
If tmp < minimum Then
minimum = tmp
Debug.Print minimum
Set rngMin = c.Offset(0, 7)
Debug.Print rngMin
End If
End If
Next c
Debug.Print minimum
Debug.Print rngMin.Address
CalcBG = rngMin.Address 'Return the address rather than the value
End Function
And the formula:
=IF(AND(AZ1>0,BA1>0,BB1>0),CONCATENATE(AZ1,BA1,BB1),CalcBG())
Place that in BG1 and copy down Column BG.
Problem 3
This added a couple more variables, but the basic structure is the same.
Based on the data, I also assumed that "If(And(AZ>0;BA>0;BB>0;BH>0;BI>0;BJ>0);0" should have been If(And(AZ>0;BA>0;BB>0;BH>0;BI>0;BJ>0),concatenate(bh,bi,bj). Otherwise your values in BO would all be 0. Also, your last column is BO, not BN. :)
Here's the macro:
Public Function CalcBO()
Dim rngCurrent As Range
Set rngCurrent = Application.ThisCell
Debug.Print rngCurrent.Address
Dim rngMin As Range
Dim minimum As Long
minimum = 100000000
Dim tmp1 As Long
Dim tmp2 As Long
Dim lowest As Long
Dim rngAZ As Range
Set rngAZ = ActiveSheet.Range("AZ1:AZ" & rngCurrent.Row - 1)
Debug.Print rngAZ.Address
For Each c In rngAZ.Cells
If c.Value2 <> 0 Then
'(AZn*BCx+BAn*BDx+BBn*BEx)-BFn
tmp1 = ((c.Value2 * rngCurrent.Offset(0, -12).Value2) + (c.Offset(0, 1).Value2 * rngCurrent.Offset(0, -13).Value2) + (c.Offset(0, 2).Value2 * rngCurrent.Offset(0, -10).Value2)) - c.Offset(0, 6).Value2
'(BHn*BKx+BIn*BLx+BJn*BMx)-BNn
tmp2 = ((c.Offset(0, 8).Value2 * rngCurrent.Offset(0, -4).Value2) + (c.Offset(0, 9).Value2 * rngCurrent.Offset(0, -3).Value2) + (c.Offset(0, 10).Value2 * rngCurrent.Offset(0, -2).Value2)) - c.Offset(0, 14).Value2
lowest = WorksheetFunction.Min(tmp1, tmp2)
If lowest < minimum Then
minimum = lowest
Debug.Print minimum
Set rngMin = c.Offset(0, 7)
Debug.Print rngMin
End If
End If
Next c
Debug.Print minimum
Debug.Print rngMin.Address
CalcBO = rngMin.Address
End Function
And here is the formula for BO1. Copy down as usual:
=IF(AND(AZ1>0,BA1>0,BB1>0,BH1>0,BI1>0,BJ1>0),CONCATENATE(BH1,BI1,BJ1),CalcBO())
There's a lot of repetition among those macros, and they could probably be condensed. But they should do what you need.
One note: The macros won't differentiate between equal minimum values. For example, in problem set 3, the two calculations (AZn ... - BFn and BHn ... - BNn) return the same value, 6, which also happens to be the minimum value among all the calculations. In this case, it returns the first address (AZn ... - BFn). So in Problem 3, you will get the answer $BG$6 in BO7, even though $BG$6 and $BO$6 both satisfy the conditions. Your requirements didn't specify what to do in case there was more than one minimum value, so I left it as it is.
Let me know if you need any help with the above.