Assume I have an input variable x and three parameters a,b,c such that:
Given b we have c = f(x,a,b) for some (known) function f
Given c we have b = g(x,a,c) for some (known, different) function g.
I want to model this in a spreadsheet (Excel for instance). More precisely, if the user provides x,a and b then c will be evaluated and if c is given then b will be evaluated. It seems like this cannot be achieved directly, since a cell can hold either a value or a formula.
Is there a canonical way to do this? If not, what would be a best-practice workaround (probably some VBA magic)?
You can separate input fields from the calculated values and add some validation that only one of the mutually exclusive field is used, e.g.:
in my example, I used following conditional formatting to highlight invalid input:
=AND($B$4<>"", $B$5<>"")
and I used following the formulas for calculated values:
=B2
=B3
=IF(AND($B$4<>"", $B$5<>""), "#ERROR: only 1 value can be specified",
IF($B$4<>"", $B$4, $B$5-1))
=IF(AND($B$4<>"", $B$5<>""), "#ERROR: only 1 value can be specified",
IF($B$5<>"", $B$5, $B$4+1))
more generally:
=if(error_condition, error_message, if(b_is_not_empty, b, g(x,a,c)))
Related
I want to consolidate the data of column B into a single cell ONLY IF the index (ie., Column A) is duplicated.
For example:
Currently, I'm doing manually for each duplicated index by using the following formula:
=TEXTJOIN(", ",TRUE,B4:B6)
Is there a better way to do this all at once?
Any help is appreciated.
There may easier way but you can try this formula-
=BYROW(A2:A17,LAMBDA(p,IF(INDEX(MAP(A2:A17,LAMBDA(x,SUM(--(A2:INDEX(A2:A17,ROW(x)-1)=x)))),ROW(p)-1,1)=1,TEXTJOIN(", ",1,FILTER(B2:B17,A2:A17=p)),"")))
Using REDUCE might be possible for a more succinct solution, though try this for now:
=BYROW(A2:A17,LAMBDA(ζ,LET(α,A2:A17,IF((COUNTIF(α,ζ)>1)*(COUNTIF(INDEX(α,1):ζ,ζ)=1),TEXTJOIN(", ",,FILTER(B2:B17,α=ζ)),""))))
For the sake of alternatives about how to solve it:
Using XMATCH/UNIQUE
=LET(A, A2:A17, ux, UNIQUE(A),idx, FILTER(XMATCH(ux, A), COUNTIF(A, ux)>1),
MAP(SEQUENCE(ROWS(A)), LAMBDA(s, IF(ISNA(XMATCH(s, idx)), "", TEXTJOIN(",",,
FILTER(B2:B17, A=INDEX(A,s)))))))
or using SMALL/INDEX to identify the first element of the repetition:
=LET(A, A2:A17, n, ROWS(A), s, SEQUENCE(n),
MAP(A, s, LAMBDA(aa,ss, LET(f, FILTER(B2:B17, A=aa), IF((ROWS(f)>1)
* (INDEX(s, SMALL(IF(A=aa, s, n+1),1))=ss), TEXTJOIN(",",, f), "")))))
Here is the output:
Explanation
XMATCH and UNIQUE
The main idea here is to identify the first unique elements of column A via ux, and find their corresponding index position in A via XMATCH(ux, A). It is an array of the same size as ux. Then COUNTIF(A, ux)>1) returns an array of the same size as XMATCH output indicating where we have a repetition.
Here is the intermediate result:
XMATCH(ux, A) COUNTIF(A, ux)>1)
1 FALSE
2 FALSE
3 TRUE
6 FALSE
7 TRUE
9 TRUE
11 FALSE
12 TRUE
15 FALSE
16 FALSE
so FILTER takes only the rows form the first column where the second column is TRUE, i.e the index position (idx) where the repetition starts. For our sample it will be: {3;7;9;12}.
Now we iterate over the sequence of index positions (s) via MAP . If s is found in idx via XMATCH (also XLOOKUP(s, idx, TRUE, FALSE) can be used for the same purpose) then we join the values of column B filtered by column A equal to INDEX(A,s).
SMALL and INDEX
This is a more flexible approach because in the case we want to do the concatenation in another position of the repetition you just need to specify the order and the formula doesn't change.
We iterate via MAP through elements of column A and index position (s). The name f has the filtered values from column B where column A is equal to a given value of the iteration aa. We need to identify only filtered rows with repetition, so the first condition ROWS(f) > 1 ensures it.
The second condition identifies only the first element of the repetition:
INDEX(s, SMALL(IF(A=aa, s, n+1),1))=ss
The second argument of SMALL indicates we want the first smallest value, but it could be the second, third, etc.
Where A is equal to aa, IF assigns the corresponding value of the sequence (remember IF works as an array formula), if not then it assigns a value that will never be the smallest one, for example, n+1, where n represents the number of rows of column B. SMALL returns the smallest index position. If the current index position ss is not the smallest one, the conditions FALSE.
Finally, we do a TEXTJOIN only when both conditions are met (we multiply them to ensure an AND condition).
In my Excel file, I have data split up over different tables for different values of parameter X.
I have tables for parameter X for values 0.1, 0.5, 1, 5 and 10. Each table has a parameter Y at the far left that I want to able to search for with a few data cells right of it. Like so:
X = 0.1
Y
Data_0
Data_1
Data_2
1
0.071251
0.681281
0.238509
2
0.283393
0.509497
0.397196
3
0.678296
0.789879
0.439004
4
0.788525
0.363215
0.248953
etc.
Now I want to find Data_0, Data_1 and Data_2 for a given X and Y value (in two separate cells).
My thought was naming the tables X0.1 X0.5 etc. and when defining the matrix for the lookup function use some syntax that would change the table it searches in. With three of these functions in adjacent cells, I would obtain the three values desired.
Is that possible, or is there some other method that would give me the result I want?
Thanks in advance
On the question what would be my desired result from this data:
I would like A1 to give the value for the X I'm searching for (so 0.1 in this case)
A2 would be the value of Y (let's pick 3)
then I want C1:E1 to give the values 0.678... 0.789... 0.439...
Now from usmanhaq, I think it should be something like:
=vlookup(A2,concatenate("X",A1),2)
=vlookup(A2,concatenate("X",A1),3)
=vlookup(A2,concatenate("X",A1),4)
for the three cells.
This exact formulation doesn't work and I can't find the formulation that does work.
I've seen many questions on here relating to this topic but most seem to stop after 2 scenarios.
I'm trying to write a formula to say something like this:
IF this value is between these two values, output A. If the same value is between these other two values, output B. If the same value is between yet a further two values, output C and if the same value is between a last two further values, output D.
Here's what I have so far:
=IF(AND(Sheet1!C2>'Control Sheet'!B2,Sheet1!C2<'Control Sheet'!C2),"A",IF(OR(Sheet1!C2>'Control Sheet'!B3,Sheet1!C2<'Control Sheet'!C3),"B",IF(OR(Sheet1!C2>'Control Sheet'!B4,Sheet1!C2<'Control Sheet'!C4),"C",IF(OR(Sheet1!C2>'Control Sheet'!B5,Sheet1!C2<'Control Sheet'!C5),"D",""))))
But it only outputs "A" or "B" and stays on "B" if the value should be "C" or "D".. if that makes sense.
Is this possible in Excel? If so, what have I done wrong?
Thanks :)
Yes, this is totally possible in excel. There's a slight mistake in your equation.
Look at this part of your equation:
IF(OR(Sheet1!C2>'Control Sheet'!B3,Sheet1!C2<'Control Sheet'!C3),"B",...
If the value is > the min value for B OR the value is < the max value for B then it outputs "B". So if the value doesn't fall within the range for A, then it will always meet these conditions for B.
The fix is to change the ORs to ANDs. The value must be > the min value AND < the max value.
=IF(AND(Sheet1!C2>'Control Sheet'!B2,Sheet1!C2<'Control Sheet'!C2),"A",IF(AND(Sheet1!C2>'Control Sheet'!B3,Sheet1!C2<'Control Sheet'!C3),"B",IF(AND(Sheet1!C2>'Control Sheet'!B4,Sheet1!C2<'Control Sheet'!C4),"C",IF(AND(Sheet1!C2>'Control Sheet'!B5,Sheet1!C2<'Control Sheet'!C5),"D",""))))
I have a cell (Say B16) that has a dropdown list with three options, depending on which one is picked I would like Cell D16 to return a value depending, i.e. if you Pick A return 0, B returns 1 and C returns 2.
I have tried multiple IF, OR, LOOKUP but nothing is working.
Any help would be fantastic.
Make a hidden sheet called mapping with the following:
Now it's just =VLOOKUP(B16,mapping!A:B,2,0). The advantage of this over the nested IF solution is that it's trivial to add more options and easier to read / edit in my option.
also you can use column A of mapping to populate your dropdown list.
Personally I don't like nested IFs as they are hard to read. If you want the transformation to be to {0, 1, 2} then you could use
=CODE(B16)-CODE("A")
This is idiomatic in programming languages using ASCII encoding. You can generalise this if you use CHOOSE. If you want {a, b, c} then use
=CHOOSE(1 + CODE(E3) - CODE("A"), a, b, c)
where a, b, and c are the values that you want: {0, 1, 3} in your case.
How about:
=IF(B16="A",1,IF(B16="B",2,IF(B16="C",3,"")))
This is for a return of {1,2,3}, modify for any three values you would like.
I have a set of batch numbers in a sheet which are alphanumeric code as follows
sdc234
fgh345
ght587
jki876
The alphabets of the batch number represent a product code. For example
sdc = 20499999
fgh = 45999999
ght = 67999992
jki = 56700000
The above relation is in another sheets.
I want to match product code with batch number directly. How do i lookup a product code based on this partial info ?
You can sort your second table in an alphabetical order and use VLOOKUP with TRUE (approximate match) as your third argument.
Assuming the second table is in column A and B:
D E
sdc234 =VLOOKUP(D1,A:B,2,TRUE)
fgh345 =VLOOKUP(D2,A:B,2,TRUE)
ght587 =VLOOKUP(D3,A:B,2,TRUE)
jki876 =VLOOKUP(D4,A:B,2,TRUE)
The output is as below:
D E
sdc234 20499999
fgh345 45999999
ght587 67999992
jki876 56700000
EDIT:
Assuming your product code is always 3 letters, you can use the LEFT function to get the first 3 letters and then use that as the lookup value. This way you can use the exact match as your third argument:
sdc234 =VLOOKUP(LEFT(D1,3),A:B,2,FALSE)
fgh345 =VLOOKUP(LEFT(D2,3),A:B,2,FALSE)
ght587 =VLOOKUP(LEFT(D3,3),A:B,2,FALSE)
jki876 =VLOOKUP(LEFT(D4,3),A:B,2,FALSE)
Credits to Mladen Savic's comment for making me think of this solution.