How to implement an advanced look up macro in excel? - excel
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.
Related
Adding the Last Few Features to my Scheduling Template Generator
Thanks to some wonderful people here on StackOverflow I have almost completed the code for my scheduling template generator! I just have three more things I would like to add that I am having some trouble with. Current Breakdown: I have two sheets - "2 - Staff Listing" and "X - Template" -The Values in in the staff listing sheet are a column of staff names beginning in Cell D9 -Then the Staff values are transferred to the template sheets starting in Cell B6 -There is a drop-down menu in the template sheet in Cell C2 where you can select which template you want to make just as an example it could read 5 Week or 5 Week with AM/PM -Based on this Key I have it repeating each staff member's names X amount of times (5 in this case) and double that if AM/PM is selected so 10 times in this case What I am hoping to still achieve: -In the template sheet I would like column C to be the week number and column D to show AM/PM. -So, for a 5 week AM/PM Template you would have Column C staring in cell C6 list Week 1, Week 1, Week 2, Week 2, up to Week 5. So, once for each week in the Key if it is AM/PM it should appear twice once for AM and once for PM. -Then I would like it to list AM starting in cell D6 and then go PM and just keep repeating for the length of the names in column B. Additionally, if it is not an AM/PM template then I would like to hide column D. Below is the code I currently have. Sub populate_Template() Worksheets("X - Template").Range("B6:K1000").ClearContents Dim SourceData As Range Set SourceData = Sheets("2 - Staff Listing").Range("D9") Dim RepititionCell As Range Set RepititionCell = Sheets("X - Template").Range("C2") Dim Destination As Range Set Destination = Sheets("X - Template").Range("B6") Dim lr As Long Dim arr As Variant With SourceData.Parent lr = .Cells(.Rows.count, SourceData.Column).End(xlUp).Row arr = .Range(SourceData, .Cells(lr, SourceData.Column)).Value End With Dim repetition As Long If Right(RepititionCell, 5) = "AM/PM" Then repetition = Split(RepititionCell.Value, " ")(0) * 2 Else repetition = Split(RepititionCell.Value, " ")(0) Dim newarr() As String ReDim newarr(1 To (lr - SourceData.Row + 1) * repetition, 1 To 1) Dim count As Long count = 0 Dim i As Long For i = LBound(arr) To UBound(arr) Dim y As Long For y = 1 To repetition count = count + 1 newarr(count, 1) = arr(i, 1) Next y Next i Destination.Resize(UBound(newarr) - LBound(newarr) + 1).Value = newarr End Sub
First you need to dim newarr to have three columns instead of one: ReDim newarr(1 To (lr - SourceData.Row + 1) * repetition, 1 To 3) Then your For y Next loop should fill in those other two columns. You can use the Mod operator to determine if you're on an odd or even row. For y = 1 To repetition Count = Count + 1 newarr(Count, 1) = arr(i, 1) 'if you're doubling up. You may want to put this in a Boolean variable 'to reduce code duplication If Right(RepititionCell, 5) = "AM/PM" Then 'if it's an odd number If y Mod 2 = 1 Then newarr(Count, 2) = "Week " & (y + 1) / 2 newarr(Count, 3) = "AM" Else 'even number newarr(Count, 2) = "Week " & y / 2 newarr(Count, 3) = "PM" End If Else 'not doubling up, so y is the week number and nothing in column D newarr(Count, 2) = "Week " & y End If Next y Finally, you have to change your write line to account for the new columns Destination.Resize(UBound(newarr, 1), UBound(newarr, 2)).Value = newarr I got rid of the LBound part of the Resize because you're starting at 1 anyway. It's less robust if you happen to start your array at some other number. But between the likelihood that would happen and the improved readability, I think it's better.
Excel index match with a condition to choose a specific column in a 3d matrix (formula or VBA)
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
Find cell before last that is greater than 0
I have the following code in VBA to find the last cell inside a range that is greater than 0: Set myRange = .Range(.Cells(1, 14), .Cells(1, 23)) count = 0 'Counter For Each cll In myRange If cll.Value > 0 Then count = count + 1 NoZeroDir = cll.Address End If Next It gets the address of the last cell greater than 0 in that range. But, how could I get the address from the cell greater than 0 before this last one? I was thinking of using an offset but that way I'd get the cell before the last > 0 but this cell could not be > 0. To illustrate it a bit, as an example I have: 2 3 5 0 1 7 0 8 1 0 1 The address from the last cell > 0 would be (1,11) but I want the cell before that one > 0, that is (1,9), not (1,10) as this is 0.
To find the second last number that is >0 Option Explicit Public Sub FindSecondLastValueGreaterZero() Dim MyRange As Range Set MyRange = Range("A1:K1") Const MAXSKIPS As Long = 1 ' skip 1 number that is >0 Dim Skips As Long Dim iCol As Long For iCol = MyRange.Columns.Count To 1 Step -1 If MyRange(1, iCol).Value > 0 And Skips < MAXSKIPS Then Skips = Skips + 1 ElseIf MyRange(1, iCol).Value > 0 Then Debug.Print "Found at: " & MyRange(1, iCol).Address Exit For End If Next iCol End Sub This will start in K loop backwards until it finds a 0 then keeps doing it until skipped >0 is 1 and print the address I1 as result. Since this loops backwards from right to left it should find the result (in most cases) faster than your code.
Alternative using Worksheetfunction Filter() (vs. MS 365) Based upon the newer WorksheetFunction Filter() (available since version MS/Excel 365) and using OP's range indication =FILTER(COLUMN(A1:K1),A1:K1>0) you are able to get an array of column numbers from cells greater than zero (0) via an evaluation of the generalized formula pattern. If you get at least two remaining columns (i.e. an upper boundary UBound() > 1) you get the wanted 2nd last column number by i = cols(UBound(cols) - 1) and can translate it into an address via Cells(1, i).Address. Public Sub SecondLastValGreaterZero() 'a) construct formula to evaluate Const FormulaPattern As String = "=FILTER(COLUMN($),$>0)" Dim rng As Range Set rng = Sheet1.Range("A1:K1") ' << change to your needs Dim myFormula As String myFormula = Replace(FormulaPattern, "$", rng.Address(False, False, external:=True)) 'b) get tabular column numbers via Evaluate Dim cols As Variant cols = Evaluate(myFormula) 'c) get the 2nd last column number of cell values > 0 Dim i As Long If Not IsError(cols) Then If UBound(cols) > 1 Then i = cols(UBound(cols) - 1) End If 'd) display result If i > 0 Then Debug.Print "Found at column #" & i & ": " & Cells(1, i).Address Else Debug.Print "Invalid column number " & CStr(i) End If End Sub Example result in VB Editor's immediate window Found at column #9: $I$1
VBA - I need to lift specific figures from an excel sheet and move them to another tab
I am having some issues with my code any help would be greatly appreciated. I have a range of data in a tab called Wheel Diameters and I need the following criteria to be met (Columns B6 to B28, G, L need to be less than 4000 and the cell values need to be 800 or less) the second is (Columns B35 to B54, G, L need to be more than 4000 but less than 5000 and the cell values need to be 800 or less) the third is (Columns B61 to B92, G, L need to be 9000 or greater and the cell values need to be 800 or less) for the information to be lifted into another tab called Wheel Diameters league. The less than 4000 need to go into the wheel diameters league tab in columns A and B, the greater than 4000 but less than 5000 need to go into columns D and E, the greater than 9000 need to go into columns G and H. Sub BUTTON5_Click() Dim c As Range Dim j As Integer Dim Source As Worksheet Dim Target As Worksheet 'Change worksheet designations as needed Set Source = ActiveWorkbook.Worksheets("Wheel Diameters") Set Target = ActiveWorkbook.Worksheets("Wheel Diameters League") Target.Range("A2:B5000").Clear j = 2 ' Start copying to row 2 in target sheet For i = 1 To 3 'Number of ¿wees? For Each c In Union(Source.Range(Cells(6, 5 * i - 2), Cells(28, 5 * i + 1)), Source.Range(Cells(35, 5 * i - 2), Cells(54, 5 * i + 1)), Source.Range(Cells(61, 5 * i - 2), Cells(92, 5 * i + 1))) If c.Text < 4000 And c.Text <= 800 Then Target.Cells(j, 1) = Source.Cells(c.Row, 5 * i - 3) Target.Cells(j, 2) = Source.Cells(5, c.Column) j = j + 1 ElseIf c.Text <= 800 And c.Text >= 4000 Then Target.Cells(j, 4) = Source.Cells(c.Row, 5 * i - 3) Target.Cells(j, 5) = Source.Cells(5, c.Column) j = j + 1 End If Next c Next i End Sub Example of how it looks: 3405 - As it is greater than 800 the details Column 1 and 3405 will be lifted and copied into a second tab called 'Wheel Diameters League'
This works on your example data: Dim Source As Worksheet Dim Target As Worksheet Dim rg As Range Dim x As Long, y As Long, z As Long, tcol As Long, threshold As Long Set Source = ActiveWorkbook.Worksheets("Wheel Diameters") Set Target = ActiveWorkbook.Worksheets("Wheel Diameters League") Set rg = Source.Range("B6:P92") For x = 1 To rg.Columns.Count Step 5 For y = 1 To rg.Rows.Count For z = 1 To 4 Select Case rg.Cells(y, x) Case 0 To 3999 tcol = 2 ' column 'B' threshold = 800 Case 4000 To 8999 tcol = 5 ' column 'E' threshold = 864 Case Else tcol = 8 ' column 'H' threshold = 849 End Select If rg.Cells(y, x + z) > 10 And rg.Cells(y, x + z) <= threshold Then With Target.Cells(Rows.Count, tcol).End(xlUp) .Offset(1, 0) = rg.Cells(y, x) 'write Coach No .Offset(1, 1) = z 'write Axle No .Offset(1, 2) = rg.Cells(y, x + z) 'write value End With End If Next z Next y Next x I've added a new variable called tcol, which is the target column we're writing the league data to. I've created a Select Case to decide what column it's set to, based on the axle class. I've added threshold which is also based on axle class. I've expanded the range down to P92 to read ALL the data. I've added a test to ensure the value is above 10, not just below threshold. This prevents cells that are not axle data, but are axle headings from triggering the writing to the league table. I've made the For.. Next loops for x and y more dynamic, in case your tables change size - in which case you just need to alter the rg range. Lastly, I've added in the writing of the value to the table, as I hadn't noticed that requirement before.
Excel if value appears in any column, average the reference column
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