One can generate a dynamic array of boolean values testing where a single value appears in a vector like so: =range=value. But is there a way to generate a dynamic array from one formula that tests where any of a multiple of values appear in the vector?
For example, in the data below, I can get the output I want in column J, which tests where the value "a" appears in my original range in column G with =$G$2:$G$7=$I$2. But if I try to test multiple values, say either "a" or "b", using the same method, I can't.
Such a solution would need to be scalable; my example ranges are small for illustration, but in practice I'd like to test where any of say 100 values exist in a range of 1000.
Use isnumber(match()):
=ISNUMBER(MATCH(A1:A6,C1:C2,0))
Related
Excel doesn't seem to let you use function such as RANK/COUNTIF on a FILTER(...) formula without first having the output of the filter somewhere in the spreadsheet. Ie, functions take range as argument but not arrays.
Let's assume in Column A, I have number 1 to 10 on the ten first rows
and in column B a filter on even numbers, =FILTER(A1:A10,MOD(A1:A10,2)=0)
If I want to count the number of rows of array returned by the filter function, I can use =COUNT(B1#)
If I want to count only the values above 5 using COUNTIF, this will also work =COUNTIF(B1#, ">5")
Now, I would like to get rid of the intermediary B Column.
For the COUNT function, it works. =COUNT(FILTER(A1:A10,MOD(A1:A10,2)=0)) returns 5.
For the COUNTIF function, it doesn't work anymore. I assume this because it takes a range as a parameter and not an array.
Excel warns in a popup: "There's a problem with this formula".
Is there a workaround this without using a temporary column to store the array? It seems I can only use functions that takes an array as parameter on top of the filter function.
EDIT:
I have used COUNTIF to build a simple example but I am more interested in other functions such has RANK.
COUNTIF was not the best example because you can obviously move the IF conditions in the FILTER conditions. Thank you #JvdV
I have a column containing strings. For each row in the column, I want to check if the string starts with one of several strings contained in an array. This array can contain any number of strings to check against the column.
The output I want is an array {TRUE;FALSE;TRUE;TRUE;etc} where it returns TRUE if the string begins with any of the strings contained in the array.
Anyone know how to do this? Thanks!
Edits for clarification: Office 365 (version 2109). I can't share the spreadsheet itself, but the column contains values like {1.1;1.1.1;1.1.2;1.2;1.3;etc}, and the arrays in question are things like {1.1;1.2}, so I want it to know which cells in the column begin with 1.1 or 1.2 in that case. So it's basically a hierarchically ordered list and I want to be able to pull into an array whether a given row of data is either a child of or is itself any of the items in the array.
Ideally it would be a formula only solution but I'm cool with VBA if it's necessary.
Example data
Bear in mind that in the real data there are many cells in column B which include way more than just two entries.
Assuming the first entry to be checked is in A1:
=0+LEFT(A1,LEN(MyArray))=MyArray
placed somewhere within the worksheet, will produce a spill array of the same dimension as MyArray and comprising the required Booleans. I leave it to you to decide what to do with that resulting array.
We can use MMULT and MATCH to return the correct array:
=LET(
lkprng, FILTER(A:A,A:A<>""),
depcll, B2&CHAR(10),
depclm, FILTERXML("<a><b>"&SUBSTITUTE(depcll,CHAR(10),"</b><b>")&"</b></a>","//b"),
dep, TRANSPOSE(depclm&""),
mtch, --(ISNUMBER(MATCH(LEFT(lkprng,LEN(dep)),dep,0))),
mmt, MMULT(mtch,SEQUENCE(COUNTA(dep))),
TEXTJOIN(CHAR(10),,FILTER(lkprng,mmt>0,"")))
Columns A & B contain a sample of data, with shop being an identifier (only two shown here)
I've setup two ranges in columns D & E which I can use the DSTDEV() function in column G
e.g. G2 formula is =DSTDEV($A$1:$B$9,2,D1:E2)
and G4 formula is =DSTDEV($A$1:$B$9,2,D4:E5)
so the output currently is:
But in reality I have a tonne of shop identifiers so I wanted to be able to use the DSTDEV() as a flood-fill formula, ideally with an output like so:
Where I could calculate the standard deviation in column E for each shop in column D
I basically wanted DSTDEV() to work like SUMIF() but the criteria has to be a range, and I'm looking for a way round that?!
I thought I would ask before I go creating a UDF to do what I needed!
I tried supplying a split range in the format e.g. (E1,E3) as the criteria but that didn't work
An option would be using Pivot Tables:
Subtotal and total fields in a PivotTable
StDev: An estimate of the standard deviation of a population, where the sample is a subset of the entire population.
This will return exactly the result you want, besides it's really easy to manage and setup
Another option would be use of the STDEV.P function integrated with FILTER. This relies on two premises:
STDEV.P calculates the population standard deviation based upon a list of values
FILTER can generate a dynamic array of values based on a single criteria when compared against a query list
So a FILTER array would look like this:
Now that the list of individual values have been generated, this can simply be wrapped with the STDEV.P (or any other function based on lists) like this:
From there, you can anchor all elements except for the bold cell, and then flood fill accordingly:
=STDEV.P(FILTER(B2:B9,A2:A9=**D2**,))
Alternatively, it may be useful to include headers and named dynamic ranges via PivotTable, and you could avoid anchoring the formula.
I am trying to figure out how to reference just one area of a named formula and return it as an array. This is so I can then count the number of rows in the areas referenced and eventually sum them. I going to eventually integrate the results of this into a pretty complicated mess of other formulas that automatically join and rank multiple matrices. I am trying to do this using formulas, not VBA, as a portability requirement. Some folks are a bit weary about running other folks code...
For now, though, I've come up with a simple example. Let's pretend that in the name manager we have a formula named Letters that is defined as:
=A1:A4,C1:C6
The range A1:A4 contains the letters "A" through ."D" and the range C1:C6 contains the letters "E" through "J".
If I write a simple INDEX formula I can return the first or second area of Letters like so:
=INDEX(Letters,,,1)
=INDEX(Letters,,,2)
I know this works by doing an F9 in on the formula and it returns the expected array of letters ({"A";"B";"C";"D"} or {"E";"F";"G";"H";"I";"J"}) for the appropriate area. But doing it this way makes the assumption that there will always be two areas in Letters. I'd like to keep my formula dynamic in case I was to add another area. I can create another formula named Letters_Areas and make it equal to the following:
=ROW(INDEX(Sheet1!$A:$A,1):INDEX(Sheet1!$A:$A,AREAS(Letters)))
This will return an array with the value of {1;2} for the example (or more if there was more areas) and I can pass that to an IF to loop like so:
=IF(Letters_Areas,INDEX(Letters,,,Letters_Areas)
But that doesn't work. It always returns just the first area in Letters because Letters_Areas in the second argument of the IF always returns 1 as the value, not the first and then second value of the array. I guess my question in a formula is:
=IF(Letters_Areas,INDEX(Letters,,,What_Do_I_Put_Here))
Where What_Do_I_Put_Here counts up for each iteration of the IF like a For loop would in VBA. Essentially, I need to be able to get i in my For i = 1 to 2 in this case inside the IF.
I know that the failure is the Letters_Areas in the second argument of the IF because I can test it. At first glance you would just do it as such:
=IF(Letters_Areas,Letters_Areas)
This returns the expected {1,2}. However, this is misleading because you can find the true behavior by doing this:
=IF(Letters_Areas,INDEX(Letters_Areas,Letters_Areas))
And this always returns {1,1} which tells me that is the part that is failing.
The final version of the formula, minus the part I cannot figure out should look something like:
=IF(Letters_Areas,ROWS(INDEX(Letters,,,What_Do_I_Put_Here)))
And in our test example this would return {4;6}. Again, stuck using no VBA. Any ideas?
Could you use this formula to count areas, assuming that your areas always begin in row 1 as in your example: COLUMNS(A1:Z1)-(SUM(IF(ISBLANK(A1:Z1),1,0)))? Any non-blank cells within that range will be counted as an area and will give the correct count. I am not sure if this is what you are seeking according to your question.
Similar to a question by Stephen Roy April 26, 2013, answered by Barry Houdini. I have a named range per this generic formula
{=IFERROR(INDEX(Range,SMALL(IF(
MATCH(Range,Range,0)=ROW(INDIRECT("1:"&ROWS(Range))),
MATCH(Range,Range,0)),ROW(INDIRECT("1:"&ROWS(Range))))),"")}
[obviously in 2003 I don't use IFERROR()]
"Range" itself is a named array formula.
The formula is used to pull unique entries from a range and arrange them at the 'top' of another 'range'. However, INDEX() appears to 'store' only a single value, not the array expected. Barry talked about wrapping the ROW() in another function to lose the {array}. It looks as though the first ROW() returns multiple values and works fine, it's the last ROW() which seems to be reduced to the first array entry only, thereby causing INDEX() to return only a single value. However, instead of trying to store this in memory, I select a multi-cell range for output, and use CSE, it works perfectly well. But I don't want to have to do that.
I've tried messing around with INDIRECT(), but can't get that to work at all.
Grateful for your thoughts folks, ian taylor
This might work for you. To return a list of unique values given your range x for example:
=LOOKUP(SMALL(IF(MATCH(x,x,0)=ROW(INDIRECT("1:"&ROWS(x))),MATCH(x,x,0)),
ROW(INDIRECT("1:"&SUM(IF(FREQUENCY(MATCH(x,x,0),MATCH(x,x,0)),1))))),
ROW(INDIRECT("1:"&ROWS(x))),x)
You could define this as a name called Unique and use it in other formulas eg =COUNTIF(x,Unique) returns an array of frequencies.
Addendum
Regarding INDIRECT/INDEX/ROW etc. here's my 2c (see also here). Function inputs and outputs can essentially be divided into three cases: single values, arrays or references. If a function input argument takes single values by default and an array is specified, then the result depends on the type of output of the function. If the output type of the function is:
A) a single value - an array of values is returned, one for each input value. This is the case for most functions eg =LOOKUP({1;2},{1;2;3},{"a";"b";"c"}) returns {"a";"b"}.
B) a reference - an array of references is returned. This is the case for INDIRECT/OFFSET. eg =INDIRECT("A1:A"&ROW()) returns a single element array containing a reference but this can raise errors in many functions as the references are not converted to values by default.
C) an array - a value or array is returned based on the first input of the function if evaluated normally and entered into a single cell. Or else an array is returned based on the first result for each input if entered into multiple cells. This is the case for INDEX which may return an array if one of the arguments is zero. For example =INDEX({1,2;3,4},0,{1;2}) returns {1;3} when evaluated in the formula bar, but {1;2} when array-entered into cells.
In fact Excel uses a variant data type that can take on any of these types so the details are a little more complex than this. Also there is the ability to modify arguments in place which may be why for example VLOOKUP behave like case C with arrays in the first argument but case A with the third argument.
i tried this as a defined name say, "Unique_List" so that i can use it as a Data validation list, but when i add this defined name to the data validation list, it gives an error.
=LOOKUP(SMALL(IF(MATCH(x,x,0)=ROW(INDIRECT("1:"&ROWS(x))),MATCH(x,x,0)), ROW(INDIRECT("1:"&SUM(IF(FREQUENCY(MATCH(x,x,0),MATCH(x,x,0)),1))))), ROW(INDIRECT("1:"&ROWS(x))),x)
where x is my range.
i tried transposing this formula to get the values comma-separated rather than semicolon-separated, but data validation list doesnt seem to work.
=TRANSPOSE(LOOKUP(SMALL(IF(MATCH(x,x,0)=ROW(INDIRECT("1:"&ROWS(x))),MATCH(x,x,0)), ROW(INDIRECT("1:"&SUM(IF(FREQUENCY(MATCH(x,x,0),MATCH(x,x,0)),1))))), ROW(INDIRECT("1:"&ROWS(x))),x))
Kindly advice.