Is it possible to extract the resulting value of a nested sub-function in VBA?
For example, from this INDEX-MATCH look-up function, I would like to find the value from the results from the two MATCH() functions:
=+INDEX(Sheet3!$B$5:$I$15;MATCH($A6;Sheet3!$A$5:$A$15;0);1)
I am looking for a macro that can return the value of the nested MATCH() function as an input in a VBA code. That is, I realize I could extract the MATCH() subfunction and have VBA paste it as a value. What I want is identify the value of the MATCH() function and calculate with it within my VBA script.
As I'm not quite following what you're saying, I'll take a guess... you want to use the match() scenario separately?
dim x as long, y as long, z as string
x = application.match($A" & ActiveCell & ",Sheet3!$A$5:$A$15,0)"
y = x*3 'perform some work using the row number x, knowing match of 1 = row 5 on sheet3
z = application.index(Sheet3!$B$5:$I$15," & y & ")"
Took that a step further where you can utilize said arithmetic'd match value.
Inherently, the match only provides a row number from your array, so i don't know what the "extract" you are referring to would be. If you are looking to extract a value that you're matching to, then you'll just end up with what is (from your example) in A6.
Edit: Updating code to be usable (previously just gave a concept)
dim x as long, y as long, z as string
with sheets("Sheets3")
x = application.match(ActiveCell.value,.range(.cells(5,"A"),.cells(15,"A")),0)
y = x*1 'perform some work using the row number x, knowing match of 1 = row 5 on sheet3
z = application.index(.range(.cells(5,"B"),.cells(15,"B")),y) 'This index was changed to be a SINGLE COLUMN (was B5:I15)
end with
Related
In Sheet1 I have several input values x, y and z in columns A, B and C. To simplify let's say I only have two rows with values (as in the picture).
In Sheet2 I have the thresholds; min and max-values for x, y and z, in addition to the corresponding codes.
I want to retrieve, in Sheet1, all codes where the input values fall within the thresholds (matching the values) in Sheet2. The codes do not need to be listed in the same cell if this complicates things; they can be listed in separate columns. I am also open for both formulas and VBA.
I know how to list several results using JOINTEXT if the criteria are exact matches. I also know how to list one result based on several inaccurate matches using INDEX + MATCH + <= >=. I don't know how to combine them.
I looked at these:
EXCEL index match by multiple criteria AND multiple match types?
https://exceljet.net/formula/multiple-matches-in-comma-separated-list
https://exceljet.net/formula/index-and-match-with-multiple-criteria
...and tried this:
=INDEX(F5:L8;SMALL(IF(COUNTIF(F5:F8;"<="&A5)*COUNTIF(G5:G8;">"&A5)*COUNTIF(H5:H8;"<="&B5)*COUNTIF(I5:I8;">"&B5)*COUNTIF(J5:J8;"<="&C5)*COUNTIF(K5:K8;">"&C5);ROW(F5:L8)-MIN(ROW(F5:L8))+1);COLUMN(F4));ROW(F4)+6)
...without any result.
I managed to solve it by using Jeeped's impressive code (see comments). However, since I'm using comma (,) as decimals-seperator I needed to include a small adjustmen. I changed "iOptions", "iIgnoreHeaderRows", "i" and "j" from Variant to Double, and "a" from Variant to Long. I also included the following code:
Dim counter As Integer, sizeOfPairs As Integer
Dim test As String
counter = 0
sizeOfPairs = UBound(pairs, 1)
For counter = 0 To sizeOfPairs
If TypeName(pairs(counter)) = "String" Then
pairs(counter) = Replace(pairs(counter), ",", ".")
End If
Next counter
I have a range of numbers and I need to identify if the first number of each cell is repeated anywhere in the corresponding row.
For example, in row 2 below, column 2 and column 3 both start with a 3. I know that if I do =LEFT(TRIM(cell)) to get just the first number but how do I find the rows that have repeated numbers in the row so row 1 isn't marked but row 2 is?
100|600|203|700| |
202|302|301|400|600|
Use a helper column with this as an array formula:
=SUMPRODUCT(--(COLUMN($A1:$E1)<>MATCH(INT($A1:$E1/100),INT($A1:$E1/100),0)))>0
Being an array formula it must be confirmed with Ctrl-Shift-Enter instead of Enter when exiting edit mode. If done correctly then Excel will put {} around the formula.
Consider the following UDF():
Public Function AnyDups(rng As Range) As Boolean
Dim valu() As String
Dim i As Long, L As Long
L = rng.Count
ReDim valu(1 To L)
AnyDups = False
If L = 1 Then Exit Function
i = 1
For Each r In rng
valu(i) = Left(r.Value, 1)
i = i + 1
Next r
For i = 1 To L - 1
v = valu(i)
For j = i + 1 To L
If v = valu(j) Then AnyDups = True
Next j
Next i
End Function
For example:
The code just loops through the possible combinations of left-most characters in the cells.
It should work with either text or numeric data.
One way to do it would be to use this formula as a basis:
=IF(ISERROR(FIND(LEFT(TRIM(A1),1),B1)),FALSE,"Row "& ROW(A1))
Depending on how you want to check your row, you can adapt it. You could either have one formula to check one cell (Lock the A1 reference and drag right) - which would allow you to know where the match is but take more space on the sheet.
Or, if you don't have too many cells to check in each row, you could concatenate all cells in the same formula:
=IF(ISERROR(FIND(LEFT(TRIM(A1),1),B1&C1&D1&E1)),FALSE,"Row "& ROW(A1))
I'm sure Gary's Student will have a more elegant answer though!
I am new to programming altogether, however my work assignments sometimes force me to automate some processes in Excel. Anyway, I wanted to calculate an average of n consecutive minimum values within a column for a given condition. I managed to find a code from user "Gary's Student" from July 2015, which fits just about perfect to my needs except I had to change Sums to Averages, however, since my programming skills are close to 0, I tried but hadn't succeeded to calculate those values for a given condition. For example (See picture in the following link.Excel columns example)in the column 2, the average of 3 consecutive minimum values is 44, however I'd like to add a condition for example, to calculate the average of 3 consecutive minimum values if the value/text in the first column is "B". The answer should obviously be 250.
Below is the initial VBA code:
Public Function consec(rng As Range, x As Long) As Double
Dim r As Range, i As Long, mymin As Double, temp As Double
Dim wf As WorksheetFunction
Set wf = Application.WorksheetFunction
i = 1
Set r = rng(i).Resize(x, 1)
mymin = wf.Average(r)
For i = 2 To rng.Count - x + 1
Set r = rng(i).Resize(x, 1)
temp = wf.Average(r)
If temp < mymin Then
mymin = temp
End If
Next i
consec = mymin
End Function
Any help is much appreciated.
Thanks.
To bypass vba the following array formula will achieve what you want:
=AVERAGE(SMALL(IF($A$2:$A$12=$E$1,$B$2:$B$12),ROW(INDIRECT("1:" & E2))))
Being an array formula it must be confirmed with Ctrl-Shift-Enter when exiting edit mode. If done properly excel will put {} around the formula.
The ROW(INDIRECT("1:" & E2)) part is what determines how many of the lowest amounts to average. It iterates from 1 or the lowest to what ever value is in E2, in this case 3 or the third.
The IF() statement; IF($A$2:$A$12=$E$1,$B$2:$B$12) puts together an array of values that meet the criteria, In this case column A being equal to what is in E1 or 'B'.
The SMALL() function uses the array from the IF() and the Iteration from the first to get the smallest in the array and returns those values to the AVERAGE() Function.
I have a simple array formula in excel that doesn't work in the way I wish. In columns A and B there is data (A1 is paired with B1 and so on) while in column F there is the calculation based on the parameter in column E.
In cell F1 the formula is:
{=SUM(MAX(A$1:A$9, E1)*B$1:B$9)}
What this formula does is:
=MAX(A$1:A$9, E1)*B$1 + MAX(A$1:A$9, E1)*B$2 + ...
Instead, I need a formula that does this:
=MAX(A$1, E1)*B$1 + MAX(A$2, E1)*B$2 + ...
In words, the formula I wrote (the first one) always finds the max between the values from A1 to A9 and E1, multiplies it by the i-th B value and sums the results. What I need is a formula that finds the max between the i-th A value and E1, and not between all the A values.
What I'm looking for is easily done by adding in column C the formula =MAX(A1;E$1)*B1 and then in F1 just =SUM(A1:A9), but I can't use this solution because in column F the same formula is repeated, with the E parameter changing every time.
I can use a IF instruction: in F1 I can write
{=SUM(IF(A$1:A$9>E1, A$1:A$9, E1)*B$1:B$9)}
While this formula does what I need in this case, I think it's a bad solution because I find it difficult to read and to expand. For example, if there is another parameter in column D and the factor is MIN(MAX(A$1:A$9;E1);D1), using IF will result in a very long and very unreadable and complicated formula.
Are there better solutions to my problem? Thank you all!
NOTE: syntax may vary a little because I am using the italian version of excel.
The problem is that MAX takes an array as an argument. Functions that normally take an array never return an array - they were designed to turn an array into one number. No matter how many arrays you throw at MAX, it's always just going to return one number.
I couldn't come up with a good solution, so here's a bad one
=SUMPRODUCT(((A1:A9*(A1:A9>E1))+(E1*(A1:A9<=E1)))*B1:B9)
I don't think that really increases the maintainability of the IF-based formula that you're trying to avoid. I think you're stuck with IF or a helper column.
Another possibility is a VBA function.
Public Function SumMaxMin(rRng1 As Range, rRng2 As Range, ParamArray vaMinMax() As Variant) As Double
Dim rCell As Range
Dim dReturn As Double
Dim aMult() As Double
Dim lCnt As Long
Dim i As Long
ReDim aMult(1 To rRng1.Cells.Count)
For Each rCell In rRng1.Cells
lCnt = lCnt + 1
aMult(lCnt) = rCell.Value
For i = LBound(vaMinMax) To UBound(vaMinMax) Step 2
If Not Evaluate(aMult(lCnt) & vaMinMax(i + 1) & vaMinMax(i)) Then
aMult(lCnt) = vaMinMax(i)
End If
Next i
Next rCell
For i = LBound(aMult) To UBound(aMult)
dReturn = dReturn + aMult(i) * rRng2.Cells(i).Value
Next i
SumMaxMin = dReturn
End Function
For your example
=SumMaxMin(A1:A9,B1:B9,E1,">")
Adding another condition
=SumMaxMin(A1:A9,B1:B9,E1,">",D1,"<")
It will error if your ranges aren't the same number of cells or you pass arguments that don't work with Evaluate.
Another possibility for avoiding repetitions of cell references is:
=SUM(B1:B9*ABS(A1:A9-E1*{1,-1}))/2
assuming values are non-negative. More generally to return an array of pairwise max values:
=MMULT((A1:A9-E1*{1,-1})^{2,1}^{0.5,1},{1;1}/2)
which returns:
MAX(A1,E1)
MAX(A2,E1)
...
MAX(A9,E1)
I don't remember ever cracking this problem, but for maintainability I'd probably do something like this:
{=SUM((A1:A9<E1)*E1*B$1:B$9) + SUM((A1:A9>=E1)*A1:A9*B$1:B$9)}
If I understand the problem correctly, using IF instead of MAX should do:
=SUM(IF($A$1:$A$9>E1;$A$1:$A$9;E1)*$B$1:$B$9)
I have some cells with, for an example, this value:
5*A-2*B-4*C
In other cells I would put the values for A, B and C. I would like to make additional cell that would count the value. So, for an example, if in some cells it is written that the value of A is 2, value of B is 3 and value of C is 1, I would like an additional cell that would calculate and put the value 0 (that's the result of 5*2-2*3-4*1). Possible variables are A, B and C, but they don't have to be contained in every cell (e.g., some cell may be just 5*A-3*C).
Is that possible? Does anyone know how to write that function?
P.S. I can't split manualy values in different cells because there are hundreds of them.
Thanks.
The comment from Saladin Akara points the way to your simplest solution. Define some named formulas (see the Excel help topic "Working with names") for A, B, and C. Then any other cells can contain formulas that use those named values.
If that's not enough, for example if you really want to see and edit the formula in a cell, and then calculate the value of the formula in a different cell, you can use Excel's built-in evaluator without having to parse the formula yourself. The easiest way to do so is via the Evaluate method of the Application object. (Again, see the help.) Charles Williams has example code on his website that evaluates an Excel expression here: http://www.decisionmodels.com/calcsecretsh.htm
Going beyond that, you can use Application.Evaluate to evaluate expressions with (scalar) parameters without defining any names, and still without actually parsing the formula, by doing some rudimentary string replacement. There are several examples on the web, but a very good one from Doug Jenkins is here: http://newtonexcelbach.wordpress.com/2008/04/22/evaluate-function/
This can be done with the VBA function EVALUTE
simple example:
Function ev(f As Variant, A As Range, B As Range, C As Range) As Variant
Dim s As String
s = f
s = Replace(s, "A", "~A~")
s = Replace(s, "B", "~B~")
s = Replace(s, "C", "~C~")
s = Replace(s, "~A~", A.Address)
s = Replace(s, "~B~", B.Address)
s = Replace(s, "~C~", B.Address)
ev = Evaluate(s)
End Function
eg if your expression is in A2, and the values of A, B, C are in C2:E2
=ev(A2,C2,D2,E2)
returns the calculated value
You would want to use a ParamArray instead of A, B, C the variable values to allow for an arbitrary number of variables.
A slightly more complete version:
Function ev(expr As Variant, VarNames As Range, varValues As Range) As Variant
Dim s As String
Dim i As Long
s = expr
For i = 1 To VarNames.Columns.Count
s = Replace(s, VarNames.Cells(1, i), "~" & VarNames.Cells(1, i) & "~")
Next
For i = 1 To VarNames.Columns.Count
s = Replace(s, "~" & VarNames.Cells(1, i) & "~", varValues.Cells(1, i).Address)
Next
ev = Evaluate(s)
End Function
Usage:
Same data as above plus variable names in C1:E1
=ev(A2,C1:E1,C2:E2)