EXCEL - Array formula match in list - excel

I am trying to make a formula that returns into a single cell (array formula) a vector of True/False based on whether each element in an array matches (identically) or not any of the elements in another array.
Example:
Array to be matched (array_compare): [A;A;B;C;D]
Array with elements (array_elements): [A;B;D]
The formula should return something like:
={formula(array_compare;array_elements)} ==> [TRUE;TRUE:TRUE;FALSE;TRUE]
I need this mid-step function so later on I can add rows or columns based on criteria or tell how many matching item there are in array_compare.
For example (for later use):
=sum(--formula(array_compare;array_elements))} ==> 4 (in the example)
THANKS!

You can use ISNUMBER(MATCH())
=SUMPRODUCT(--ISNUMBER(MATCH({"A","A","B","C","D"},{"A","B","D"},0)))
This will return 4 as it will iterate the large array.
If you want to iterate the smaller array reverse the two arrays and it will return 3:
=SUMPRODUCT(--ISNUMBER(MATCH({"A","B","D"},{"A","A","B","C","D"},0)))

Related

How to use named range in an Excel array?

Basically the problem boils down to - how do I use a named reference/range within array in an Excel spreadsheet formula?
Example:
={"this","is","my","house"}
Produces 4 cells in one row with correct text
But this
={"this","is","my", House}
where House is a named range of a cell containing some text fails.
Your attempt failed, because of the array notation {}.
An array, typed like that is limited to number values and/or text strings, for instance {"a",1,"b"}.
A range can not be used inside an array notation neither can a named range.
To avoid the array notation and still get the array to include the named range you can make use of VSTACK or HSTACK who both create arrays or append arrays even.
In this case your array {"this","is","my"} can be used inside HSTACK and the named range House can be appended:
=HSTACK({"this","is","my"},House)
This will give the desired result, but as HSTACK creates arrays by appending numerous values/ranges/arrays, we no longer need the {}:
Proper notation: =HSTACK("this","is","my", House)
Would be the proper notation.
If you would not have access to HSTACK, but have access to LET, you could use this little more complex solution:
=LET(a,{"this","is","my"},
b,House,
count_a,COUNTA(a),
seq,SEQUENCE(1,count_a+1),
CHOOSE(IF(seq<=count_a,1,2),a,b))
First a (the text array) and b (the named range) are declared.
Then count_a is declared, which counts the number of strings in array a (3).
Then seq is declared to create a (horizontal) sequence from 1 to the count of strings in a (count_a) and adding 1 (resulting in {1,2,3,4}.
Then calculating if the sequence seq is smaller than or equal to the count of strings in a results in TRUE for the first 3 values of the sequence and false for the fourth: {TRUE,TRUE,TRUE,FALSE}.
Using that in combination with IF (if TRUE 1, else 2) results in an array of {1,1,1,2}.
Using that as the CHOOSE argument results in the 1st 3 times choosing values from a and the 4th time (the first) value of named range b.
Not using LET and SEQUENCE will result in a very unmanageable formula, which would require more work fixing the values within the formula then just typing them out, probably, but this would create the array in older Excel versions:
=CHOOSE(
IF(
COLUMN($A$1:
INDEX($1:$1048576,,COUNTA({"this","is","my"})+1))
<=COUNTA({"this","is","my"}),
1,
2),
{"this","is","my"},
House)
Requires entered with ctrl+shift+enter and would appear as being one value only, because older Excel doesn't spill arrays into a range, but the array could be referenced inside a formula or as a named range.
Here COLUMN($A$1:INDEX($1:$1048576,,COUNTA({1,2,3}))) simulates the sequence function.
If you have access to Excel O365:
With HSTACK, it would simply be =HSTACK("this","is","my",house).
House could be a single value or an array. If "House" is a named range {"A","B,"C"} then the HSTACK function above returns a 6 element array {"this","is","my","A","B,"C"}

Condensing a large formula

I was wondering if anyone could enlighten me on a way to condense/shorten this formula:
=IF(ISNUMBER(SEARCH("Kirkintilloch",B2)),"BRN01",IF(ISNUMBER(SEARCH("Tweacher",B2)),"BRN01",IF(ISNUMBER(SEARCH("Lenzie",B2)),"BRN01",IF(ISNUMBER(SEARCH("Bishopbrigg",B2)),"BRN03",IF(ISNUMBER(SEARCH("Torrance",B2)),"BRN03",IF(ISNUMBER(SEARCH("Bearsden",B2)),"BRN04",IF(ISNUMBER(SEARCH("Milngavie",B2)),"BRN04")))))))
Column B will contain an address which will be from one of these seven towns.
The reason I didn't do an IF then Lookup was that the result I want to return is not unique, I.e. Kirkintilloch & Torrance both need to return a result of BRN01.
If it's not possible to simplify this then no worries. It would just save me a lot of work in a larger piece of work with many more possible outcomes.
This is an array formula - confirm it with Ctrl+Shift+Enter while still in the formula bar:
=INDEX(Towns,SMALL(IF(ISNUMBER(SEARCH(INDEX(Towns,0,2),B2)),INDEX(Towns,0,1)),1),3)
Breaking this down:
First you need to create a named range (I called mine "Towns") that contains the array of an index, the town name and the desired return "BRN**" (you could make this a simple range and just reference that but I entered the actual array by selecting the range in formula, highlighting it and using F9 to calculate)
Now that you have this array, I use Index providing 0 for the row argument to return all rows so that I can equate against just a single column at a time.
IF(ISNUMBER(SEARCH(INDEX(Towns,0,2),B2)),INDEX(Towns,0,1))
As this is an array formula, each row is assessed individually and the results (first column when a match is found - index of the array) returned as an array, like so: {FALSE;FALSE;3;FALSE;FALSE;FALSE;FALSE}
I then use SMALL() to catch the lowest number or the first match to feed another index for the third column.

how to specify cell value according to its content in sum formula

I have a column contains some texts as follow:
one
two
three
four
I want to sum the values this column cells according to their content, so I should check the content then return a value, as if(cell = one) then 1
so the sum result should be 1+2+3+4 = 10
I tried to do a formula like =SUM(IF(A1=apartment,1),...) but its absolutely wrong.
how can I write this formula?
You can also do:-
=SUMPRODUCT((A1:A10={"One","two","three","four"})*{1,2,3,4})
This builds up a 2d array where the rows correspond to your data and the columns correspond to the strings "one","two","three" and "four". The elements are set 'true' only where the data matches one of the four strings. Then this array is multiplied by the row of numbers 1,2,3 and 4. 'TRUE' counts as 1 in the multiplication and 'FALSE' counts as 0.
Count the words and multiple by the associated values:
=COUNTIF(A:A,"one")+2*COUNTIF(A:A,"two")+3*COUNTIF(A:A,"three")+4*COUNTIF(A:A,"four")+5*COUNTIF(A:A,"five")
You can extend this formula by adding more terms if necessary, or use a VLOOKUP() table.

Excel: Map a function across a 2-d array, then call a function on the resulting column array

I'd like to use a single formula in excel without using VBA or macros to traverse a 2-dimensional array and produce a single value. The formula would do the following:
First, iterate through every row of the data, calling a function (in my case, MAX) on each row. This will return a column of values. Then call a single function (in my case, SUM) on that column of values. This should return a single value.
In programming terminology, if I am given a 2-dimensional array of values in row-major-order, I'd like to map the function MAX across the 2-d array, and then call SUM on the resulting 1-d array.
Is there a way to do this in excel in a single cell formula?
Thanks so much. Please let me know if I can make this question clearer in any way, or if I should be asking this question somewhere else.
Such requirements are normally achieved with array formulas. But there is no universally usable solution for all such requirements. For functions which are usable with SUBTOTAL, this could be achieved like follows:
{=SUM(SUBTOTAL(4,OFFSET($A$2:$E$2,ROW($A$2:$A$6)-ROW($A$2),0)))}
This is an array formula. Enter the formula in the cell without curly brackets and press [CTRL]+[SHIFT]+[ENTER] then. The curly brackets then should appear automatically.
How it works:
The part ROW($A$2:$A$6) gets a array of row numbers {2,3,4,5,6}. The -ROW($A$2) subtracts the start row, so the resulting array is {0,1,2,3,4}. The OFFSET part then shifts $A$2:$E$2 by {0,1,2,3,4} rows and results in a array of row vectors {$A$2:$E$2, $A$3:$E$3, ..., $A$6:$E$6}. From those the SUBTOTAL gets the MAX values per row and the SUM sums these MAX values.
Instead of SUM as array formula in this case we can also use SUMPRODUCT. This gets its parameters in array context automatically. So with this it is not necessary to enter the formula as array formula:
=SUMPRODUCT(SUBTOTAL(4,OFFSET($A$2:$E$2,ROW($A$2:$A$6)-ROW($A$2),0)))

Why doesn't LOOKUP match the first element in an array?

Here is the screenshot of my excel workbook
I do not understand why the value in cell j7 is 44 ?
j7 formula is =LOOKUP(1,(TRIM($D$2:$D$9)=TRIM(H7))/(TRIM($E$2:$E$9)=TRIM(I7)),$F$2:$F$9)
The result of the two arrays division is the following
{TRUE;TRUE;FALSE;FALSE;FALSE;FALSE;FALSE;FALSE}/
{TRUE;FALSE;TRUE;FALSE;FALSE;TRUE;FALSE;FALSE} =
{1;#DIV/0!;0;#DIV/0!;#DIV/0!;0;#DIV/0!;#DIV/0!}
Right ?
So I am looking for 1, basically the formula becomes
LOOKUP(1,{1;#DIV/0!;0;#DIV/0!;#DIV/0!;0;#DIV/0!;#DIV/0!},$F$2:$F$9)
Hence the result should be 10 but not 44 . . . . . ?
EDIT
When i correct my formula to =LOOKUP(1,1/(TRIM($D$2:$D$9)=TRIM(H7))/(TRIM($E$2:$E$9)=TRIM(I7)),$F$2:$F$9)
it works fine . Why ? Thank you everybody for giving the alternative solutions with match and index. I just can't understand why my first formula did not work. Any why when i add 1/ it MAGICALLY works ? ? ?
If the values are not in ascending order, and you are looking for a value within the range of values (as opposed to a value larger than anything in the range), LOOKUP may give unexpected results.
A different way to return the desired result is with a combination of INDEX and MATCH:
=INDEX($F$2:$F$9,MATCH(1,(TRIM($D$2:$D$9)=TRIM(H7))/(TRIM($E$2:$E$9)=TRIM(I7)),0))
entered as an array formula with ctrl-shift-enter
Note that with MATCH, looking for an exact match, the range does not need to be sorted.
Another formula that will return the correct results, and can be normally entered (assuming no duplicate entries in the first table):
=SUMPRODUCT((TRIM(H7)=TRIM($D$2:$D$9))*(TRIM(I7)=TRIM($E$2:$E$9))*$F$2:$F$9)
The explanation is:
with the first formula, the first array in the Lookup function contains zeros after the 1 value, in the third and sixth value of the array:
Lookup expects data to be sorted ascending and will return the first item less than or equal to the search value, starting from the last value in the array. In this case that is the zero value in the sixth position of the array.
The edited formula results in an array that contains only one number "1". All other values are Div errors. So the position of that "1" value is what Lookup will use.
Further explanation:
In your first formula you divide two arrays that contain TRUE or FALSE and the result contains 1, 0 and Div error values.
{TRUE;TRUE;FALSE;FALSE;FALSE;FALSE;FALSE;FALSE}/
{TRUE;FALSE;TRUE;FALSE;FALSE;TRUE;FALSE;FALSE} =
{1;#DIV/0!;0;#DIV/0!;#DIV/0!;0;#DIV/0!;#DIV/0!}
Including the 1/ in the formula will divide the first array of TRUE and FALSE values by 1, which returns an array that consists of either 1 or Div errors. Further dividing that array by the second array will only return 1 or Div errors, no zeros. The steps are
1/{TRUE;TRUE;FALSE;FALSE;FALSE;FALSE;FALSE;FALSE}/{TRUE;FALSE;TRUE;FALSE;FALSE;TRUE;FALSE;FALSE} results in
{1;1;#DIV/0!;#DIV/0!;#DIV/0!;#DIV/0!;#DIV/0!;#DIV/0!}/{TRUE;FALSE;TRUE;FALSE;FALSE;TRUE;FALSE;FALSE} results in
{1;#DIV/0!;#DIV/0!;#DIV/0!;#DIV/0!;#DIV/0!;#DIV/0!;#DIV/0!}
No zeros!
As mentioned, LOOKUP expects the values in the lookup_vector to be in ascending order. To gain the first match of columns H & I to columns D & E, I would suggest mathematically excluding the non-matching rows. What is left would be the matching rows. The following example supplies the first double match.
For J2
=INDEX($F$2:$F$9,MIN(INDEX(ROW($1:$8)+(($D$2:$D$9<>H2)+($E$2:$E$9<>I2))*1E+99,,)))
Fill down as necessary. This is a standard formula and can be easily modified to supply the 2nd, 3rd, etc matching values by swapping SMALL() in place of MIN(). Your results should be close to the following.
       

Resources