Excel coding gurus, can someone help me count in Excel? :) I need a count, based on multiple, dictionaty depandant, conditions.
What I have:
I have an Excel 2019. Not the 365 edition.
I have an Excel sheet called, say Dicts with a table of 2 columns. I is a dictionary. Column I contains list of people. And each person in the I column has the country of his/her origin denoted in the correspondent cell of H column.
For a better view check this picture, plz.
And I have a DataSheet, that contains records of various persons from the dictionary table along wiht some data on each record.
For a better view check this picture, plz.
Now, the question is:
How can I count the number of all the citizens of USA and Iitaly in the column A that have either Y or M in the correspondent cell of Column B?
You could use the following in Office 365:
=LET(condition1,FILTER(A2:A25,MMULT(--({"Y","M"}=B2:B25),SEQUENCE(2,,1,0))),
condition2,FILTER(Table1[C2],MMULT(--({"USA","Irtaly"}=Table1[C1]),SEQUENCE(2,,1,0))),
SUM(--(TRANSPOSE(condition1)=condition2)))
In Excel 2019 you could use the following:
=SUM(
--(TRANSPOSE(INDEX(Table1[C2],
AGGREGATE(15,6,ROW(Table1[C1])-1/(MMULT((--(Table1[C1]={"USA","Irtaly"})),ROW(1:2)^0)),
ROW(A1:INDEX(A:A,SUMPRODUCT(--({"USA","Irtaly"}=Table1[C1])))))))
=INDEX(A2:A25,
AGGREGATE(15,6,ROW(A2:A25)-1/(MMULT((--(B2:B25={"Y","M"})),ROW(1:2)^0)),
ROW(A1:INDEX(A:A,SUMPRODUCT(--({"Y","M"}=B2:B25))))))))
In Excel versions prior to Office 365 this formula needs to be entered with ctrl+shift+enter
Change the Table name and references to your needs (I can't reproduce the characters used and Table Name is nowhere mentioned)
When using different system language using different separators use the following:
=SUM(
--(TRANSPOSE(INDEX(Table1[Стовпець2];
AGGREGATE(15;6;ROW(Table1[Стовпець1])-1/(MMULT((--(Table1[Стовпець1]={"USA"\"Irtaly"}));ROW(1:2)^0));
ROW(A1:INDEX(A:A;SUMPRODUCT(--({"USA"\"Irtaly"}=Table1[Стовпець1])))))))
=INDEX(A2:A25;
AGGREGATE(15;6;ROW(A2:A25)-1/(MMULT((--(B2:B25={"Y"\"M"}));ROW(1:2)^0));
ROW(A1:INDEX(A:A;SUMPRODUCT(--({"Y"\"M"}=B2:B25))))))))
I can imagine you're looking for something simpler, but what about this (pseudocode, based on COUNTIFS() worksheet function):
=COUNT_Multiple_Criteria(range,country="USA", done="Y") +
COUNT_Multiple_Criteria(range,country="USA", done="M") +
COUNT_Multiple_Criteria(range,country="Italy", done="Y") +
COUNT_Multiple_Criteria(range,country="Italy", done="M")
Assuming:
The table in sheet Dicts is named loPeople with fields: Country and People
The data in sheet DataSheet is located in a table named loDone with fields: Name and Done
Try this formula:
= SUM( IFERROR( IF(
MATCH( loDone[Done], {"M","Y"}, 0 )
* MATCH( INDEX( loPeople[Country],
MATCH( loDone[Name], loPeople[People], 0 ) ), {"Ctry.C","Ctry.E"}, 0 ),
1 ), 0 ) )
Dynamic Formula: If a dynamic formula is required, let's assume that:
The Country criteria is located in a table*{a}* named: loSelCtry
The Done criteria is located in table*{a}* named: loSelDone
Try this formula:
= SUM( IFERROR( IF(
MATCH( loDone[Done], loSelDone, 0 ) *
MATCH( INDEX( loPeople[Country],
MATCH( loDone[Name], loPeople[People], 0 ) ), loSelCtry, 0 ),
1 ), 0 ) )
*{a}Using tables for the criteria enhances the dynamics of the formula.
Let's try the following approach. In cell: H4 put the following formula:
=SUMPRODUCT(
N(ISNUMBER(MATCH(INDEX(A3:A8, MATCH(D3:D7, B3:B8,0)), H2:I2,0))),
N(ISNUMBER(MATCH(E3:E7,H3:I3,0)))
)
Here is the output:
The formula uses valid functions for Excel 2019, but that is not enough, you need to validate the expected behavior. After dynamic array behavior was introduced for most of the functions the output should be different. In such cases you can try the legacy approach: Ctrl+Shift+ENTER to ensure a dynamic array output.
If that doesn't work then an alternative is to use MMULT as follow, assuming IF returns a dynamic array for your Excel version:
=SUM(MMULT(TRANSPOSE(IF(INDEX(A3:A8, MATCH(D3:D7,B3:B8,0))=H2:I2,1,0)),
IF(E3:E7=H3:I3,1,0)))
that SUM per country all names with Y or M.
Explanation
Let's use the LET function for better understanding:
=LET(lkUpC, H2:I2, lkupD, H3:J3, countries, INDEX(A3:A8, MATCH(D3:D7, B3:B8,0)),
cCnts, N(ISNUMBER(MATCH(countries, lkUpC,0))),
dCnts, N(ISNUMBER(MATCH(E3:E7,lkupD,0))),
SUMPRODUCT(cCnts, dCnts)
)
The name countries finds the corresponding countries for the Dataset columns. The name cCnts returns an array with the same number of rows as countries having 1 for LkUpC match values otherwise 0. Similarly dCnts the Done counts based on lkUpD. Both arrays have the same size, so we can invoke SUMPRODUCT to count the number of items that satisfy both conditions (columns D and E are highlighted in yellow in the above screenshot)
Related
I have 1 workbook with 2 worksheets.
The first screenshot shows Worksheet A, the second screenshot shows Worksheet B
I would like to match the corresponding quantity to the corresponding grade in Worksheet A based on the Columns "Line", "Sub-Line", "Category", "Occasion", "Sub-Cat" and "Grade" in Worksheet B.
Question: How to get the corresponding quantity?
Many thanks!
Since you already have a concatenation of the search criteria in SheetB!F:F you might use this formula if you have Excel 365 (much earlier versions won't have the TEXTJOIN function).
=VLOOKUP(J2, SheetB!$F$2:$K$1000, MATCH(I2,SheetB!$F$1:$K$1, 0), FALSE)
Witness that the MATCH function includes F1, which must not have any of the captions found in G1:K1. $K$1000 is an arbitrary row number intended to be larger than anything you need. For use in my own project, I would create a dynamic named range instead of SheetB!$F$2:$K$1000.
This is a little heavy and perhaps someone can make an easier approach, but let's give it a go. NB: This requires Excel 2019 or 365.
You can put this into your QTY column in WORKSHEET A:
=SUM(TRANSPOSE(MMULT(SIGN(SEQUENCE(1,5)),TRANSPOSE(--(B5:F5=WorksheetB!$A$3:$E$14)))=5)*INDEX(WorksheetB!$G$3:$K$14,,MATCH(G5,WorksheetB!$G$2:$K$2,0))
That's hard to read, so here it is again with formatting (it works with formatting all the same):
=SUM( TRANSPOSE( MMULT( SIGN( SEQUENCE( 1, 5) ),
TRANSPOSE(--(B5:F5=WorksheetB!$A$3:$E$14) ) ) = 5 )
* INDEX( WorksheetB!$G$3:$K$14,,MATCH( G5, WorksheetB!$G$2:$K$2,0) ) )
Seems there would be an easier way, but it does not come to me at the moment.
As a side note - what you are doing looks very much like something that could be done in Power Query. It would make quick work of doing such match-ups and aggregations and if there is a lot more that you want to do with these data, it would support that as well.
You can use INDEX and MATCH for this:
=INDEX('Worksheet B'!$G$4:$K$26,MATCH(1,($B5='Worksheet B'!$A$4:$A$26)*($C5='Worksheet B'!$B$4:$B$26)*($E5='Worksheet B'!$C$4:$C$26)*($D5='Worksheet B'!$D$4:$D$26)*($F5='Worksheet B'!$E$4:$E$26),0),MATCH($I5,'Worksheet B'!$G$3:$K$3,0))
The formula needs to be entered with ctrl+shift+enter as it's an array formula.
It indexes the quantities in 'Worksheet B'!$G$4:$K$2 and returns the row number of where the value in Worksheet A of the line, sub-line, occasion, category, sub-cat matches the value in the resembling column in Worksheet B and the indexed column number of where the grade value in Worksheet A equals the header in Worksheet B'!$G$3:$K$3.
This worked for me!
=VLOOKUP(CONCAT(B10,"-",C10,"-",E10,"-",F10,"-",D10),'Worksheet2'!$F$4:$K$26,MATCH('Worksheet1'!M10,'Worksheet2'!$F$3:$K$3,0),FALSE)
Cheers
I have a Selected Countries table in Calc Sheet with dropdown lists on each cell that allows me to select specific countries from a `Holidays table. I have written a formula in Cell E2 of Calc Sheet that selects the dates from those selected countries columns.
=IFERROR(INDEX(HolidayList[#All],ROW(HolidayList[#All]),TRANSPOSE(MATCH(CHOOSE({1;2;3;4;5},$A$2,$A$3,$A$4,$A$5,$C$2),TRANSPOSE(HolidayList[#Headers]),0))),"")
In column L, I have written a formula to merge the selected countries dates and create a single date column of Unique Distinct values (non-duplicates).
=IFERROR(SMALL(SelectedHolidays,ROW(INDIRECT("1:"&COUNT(SelectedHolidays)))),)
This formula automatically lists all the dates without me having to drag the formula down to each cell. I need someone to help me modify this formula to generate a single list of unique distinct dates column. I don't need a formula that i have to drag down to subsequent cells.
I have also listed on a NamedRanges Sheet, the named ranges i have created in the workbook.
Here is a sample workbook attached for your perusal.
Edit:
I was able to workout a unique list of dates without dragging down the formula using the FREQUENCY function. However, there are #Values (for duplicates) in the rows array produced by MATCH in formula. How do i exclude the #Values in this array?
=SMALL(MDHolidays,IF(FREQUENCY(MDHolidays,MDHolidays)>0,MATCH(MDHolidays,MDHolidays,0),""))
For your first question, you can simply hide zero values by formatting your cells as m/d/yyyy;;. However, your formula can be amended to exclude the TRANSPOSE function...
=IFERROR(INDEX(HolidayList[#All],ROW(HolidayList[#All])-MIN(ROW(HolidayList[#All]))+1,MATCH(CHOOSE({1,2,3,4,5},$A$2,$A$3,$A$4,$A$5,$C$2),HolidayList[#Headers],0)),"")
For your second question, select cells L2:L33, since there's a maximum of 32 possible values, enter the following formula, and press Ctrl + Enter...
=IFERROR(SMALL(IF(SelectedHolidays>0,IF(ISNA(MATCH(SelectedHolidays,L$1:L1,0)),SelectedHolidays)),1),"")
EDIT
I'm a little confused as to why you're using MDHolidays, instead of SelectedHolidays. In any case, you can amend the the latest formula
you've posted as follows...
=IFERROR(SMALL(IF(FREQUENCY(IF(MDHolidays>0,MATCH(MDHolidays,MDHolidays,0)),ROW(MDHolidays)-MIN(ROW(MDHolidays))+1)>0,MDHolidays),ROW(INDIRECT("1:"&COUNT(MDHolidays)))),"")
We can get rid of zeros aka 1/0/1900 in column L by adding COUNTIF(SelectedHolidays,0) to row number, getting
=IFERROR(SMALL(SelectedHolidays,COUNTIF(SelectedHolidays,0)+ROW(INDIRECT("1:"&COUNT(SelectedHolidays)))),"")
We'll get a distinct Holidays range adding an additional column (for example, M) with an array formula
=IFERROR(INDEX(MDHolidays,MATCH(0,COUNTIF(M$1:M1,MDHolidays),0)),"")
EXCEL 365 Method -----------------------------
UNIQUE treats each row or column as a "tuple" and then compares each tuple, so when you put a matrix into it such as A1:C3, it looks either row-wise (e.g. {A1,B1,C1} vs {A2,B2,C2} ) or columns-wise (e.g. {A1;A2;A3} vs {B1;B2;B3} ) to determine if a tuple is unique. It does not look at each of the cells, so you have to multiplex the cells into a single column (or row) and then apply UNIQUE to that multiplexed range.
Here is a formula that you can put into L2 that will deliver a dynamic array of unique dates based on your XLS (thanks for sharing - that made this a lot easier to understand):
=LET( range, E3:I9,
Cols, COLUMNS( range ),
Rows, ROWS( range ),
iSeq, SEQUENCE( Rows * Cols,,0 ),
RowIndex, iSeq / Cols + 1,
ColIndex, MOD( iSeq, Cols ) + 1,
rawList, UNIQUE( INDEX( range, RowIndex, ColIndex ) ),
SORT( FILTER( rawList, ( rawList <> "" ) * ( rawList > 0 ) ) )
)
I set range = E3:I9 so that it includes up to Belgium in your output - not sure if that is what you are trying to achieve.
This LET formula takes in range as a variable and then it measures its dimension.
Modulation & Multiplexing
It then prepares the modulation so that range can be multiplexed into a single column. iSeq is the sequence that counts each cell in the range and that will be modulated vertically into RowIndex and horizontally into ColIndex. These are used to multiplex range by applying INDEX( range, RowIndex, ColIndex ).
Applying UNIQUE and Filtering
rawList applies UNIQUE to the multiplexed column to arrive at the unique values but... these will contain some undesirable outputs such as blank and 0 that need to be filtered, so the result is delivered as the filter of rawList. The final output is then sorted.
Also, with this formula, you can change your count of unique dates
formula in N2 to =COUNTA(L2#).
EXCEL 2013 Method (Plumhoff-Bartholomew) ----------------------------
Your MDHolidays is now going to become a set of helper cells using the same name that you have given it (let's not rename it for now or it will get confusing). We are going to use two more helpers to:
Generate a set of unique dates
that are free of zero-value dates
First, create two new named ranges:
k is an old-school sequence generator based on row
= ROW( MDHolidays ) - 1
unique.idx is boolean mask of k that eliminates duplicates, blanks and zero-dates:
= IF( ISNUMBER( MDHolidays ),
IF( ( MATCH( MDHolidays, MDHolidays , 0 ) = k ) * ( MDHolidays <> 0 ),
k ) )
These two can be created as named ranges without placing them anywhere. Just open Name Manager and create them as new names with the formulas above.
Now, you can create your output. You can test this in cell M2. It will be:
= IFERROR( INDEX( IF( MDHolidays > 0, MDHolidays ), SMALL( unique.idx, k ) ), "" )
If you like the result, then let's do a final clean-up. Rename MDHolidays to MCHolidays (merged, consolidated holidays) and then open Name Manager and replace your current dyn range formula for MDHolidays: =OFFSET(Calc!$L$2,0,0,COUNTA(Calc!$L:$L)-1) with your current (and very good) formula in L2 =IFERROR(SMALL(SelectedHolidays,ROW(INDIRECT("1:"&COUNT(SelectedHolidays)))),). This will virtualize those values so that they don't take up any cell-space.
Now you can reuse your old MDHolidays name by opening Name Manager, creating a new MDHolidays with the formula that is currently in M2 above. To complete the clean-up, change the formula in L2 to simply = MDHolidays and delete the formula in M2.
This method of teasing out UNIQUE was developed by Bernd Plumhoff and Peter Bartholomew.
I'm looking for a way or ideas (preferable array solution), where you can determine if the numbers in a row increases.
The excel formula should give me "True" for the following sequence, since I want it to exclude #N/A values.
Example:
0,22 0,275 0,3162 0,36 #N/A 0,46 0,52
Notice:
I saw an reddit post "Formula to detect if row values are increasing?" with a similar question. I liked the idea and I have tried to use it with my numbers, but don't get the formula to work/understand it fully.
I am not sure if it can be done in one array formula, because the possibility to have N/A values causes some additional complexity. I can suggest a solution with a helper column though. Say your list of values is in A1:A7, then you can get the sign of the difference between the value in A2 and the value in A1 as follows:
= IF( ISNUMBER( A2 ), SIGN( A2 - LOOKUP( 2, 1 / ISNUMBER( A$1:A1 ), A$1:A1 ) ), 0 )
if you put this formula in B2, you can drag this down to B7. Now if you compare the sum of B2:B7 with the number of increases you expect, you have your answer:
= SUM( B2:B7 ) = COUNT( A1:A7 ) - 1
Refer to this very helpful page to get an explanation of how to get the last non-blank (c.q. numeric) value in a range.
I am creating a spreadsheet application which will require a user to fill in different sections of an input sheet. To separate out these sections into logical means, I am using headers and sub headers as seen below:
If I have a lot of headers, there will be a significant amount of manual work in numbering the headers. I have made some attempt at automating the process by creating a Header1 named range which equates to the grey headers in the picture below. So within the A2 and A11 cells, the formula is =Header1. The formula in the Header1 named range is:
IF(COUNTA(INDIRECT(CONCATENATE("$A$1",":",ADDRESS(ROW()-1,COLUMN()))))=0,0,INDEX(INDIRECT(CONCATENATE("$A$1",":",ADDRESS(ROW()-1,COLUMN()))),MATCH(ROW(INDIRECT(ADDRESS(ROW()-1,COLUMN()))),INDIRECT(CONCATENATE("$A$1",":",ADDRESS(ROW()-1,COLUMN()))),TRUE)))+1
Basically the formula counts all of the values in column A and adds 1 on. In other words it nicely increments every time you call Header1 in your cell, regardless of what worksheet you are in (hence the use of INDIRECT). The only piece of hard coding is the starting cell which is A1 and I have put a 0 in that so it detects to start from 1.
My question is - In a similar manner to how I have achieved incrementing headers using named ranges, how can I do this for sub headers? My picture above shows the effect I am trying to achieve (i.e. 2.1, 2.2) however I want this to be automatic by simply putting a formula of =Header2 in the cell for example.
EDIT - I have gotten as far as this formula:
=SUM(INDIRECT(CONCATENATE("$A$1",":",ADDRESS(ROW()-1,COLUMN()-1)))) & "." & MAX(1,COUNTA(INDEX(INDIRECT(CONCATENATE("$B$1",":",ADDRESS(ROW()-1,COLUMN()))),MATCH(SUM(INDIRECT(CONCATENATE("$A$1",":",ADDRESS(ROW()-1,COLUMN()))))-1,INDIRECT(CONCATENATE("$A$1",":",ADDRESS(ROW()-1,COLUMN()-1))),FALSE)):INDIRECT(ADDRESS(ROW()-1,COLUMN()))))
...which works when placed as a formula in a cell, but doesn't work when used in a named range. Odd!
Good idea using Defined Names to hold the formula. However your formula is highly volatile.
Suggest to create two defined names at workbook level (scope) as follows:
Named _Hdr (change as required) with the following formula:
=IF( COLUMN() <> 1, "", 1
+ MAX( INDEX(!$A:$A, 1 ) : INDEX(!$A:$A, - 1 + ROW() ) ) )
Named _Sub (change as required) with the following formula:
=IF( COLUMN() <> 2, "", 0.01
+ IF( MAX( INDEX(!$A:$A, 1 ) : INDEX(!$A:$A, - 1 + ROW() ) )
> MAX( INDEX(!$B:$B, 1 ) : INDEX(!$B:$B, - 1 + ROW() ) ),
MAX( INDEX(!$A:$A, 1 ) : INDEX(!$A:$A, - 1 + ROW() ) ),
MAX( INDEX(!$B:$B, 1 ) : INDEX(!$B:$B, - 1 + ROW() ) ) ) )
Use 0.1 if sub items are less than 10, if higher but less than 100 use 0.01 (adjust as required)
Edit: Add ! to the column ranges in the formulas to ensure that the references will automatically update to the corresponding Sheet where the formula is used.
The formulas above are broken in several lines to ease reading and understanding, enter then as one line when creating the names.
Also suggest to hide the Define Names (i.e. Visible = False) in order to avoid formulas being accidentally altered.
With the above names:
There is no need to enter zero at row 1.
The formulas work with numbers ignoring any labels.
They check first that the formula is entered in the expected column (i.e. _Hdr and _Sub columns 1 and 2 respectively).
As the formulas work with numbers they get the MAX number for each column and add 1 or 0.1 to generate the next number.
Formulas are no volatile (no use of INDIRECT).
They use the INDEX function to generate the required ranges.
I am trying to implement something similar to what is explained in this article:
http://exceltactics.com/make-filtered-list-sub-arrays-excel-using-small/
For this example, column B has Text, column c has integer values, and column d has text
The formula below works for filtering on values that are >= 1 in column C of my data:
=IFERROR(
INDEX(
B$2:B$11,
SMALL(
IF(
$C$2:$C$11>=1,
ROW(B$2:B$11)-ROW(B$2)+1
),
ROWS(B$2:B2)
)
),
""
)
I would like to replace that line with a search function that filters based the text content of a cell (from column D). The following works on a single line (returns 0 if "a" is not contained in the cell, otherwise a value greater than 0).
=IFERROR(SEARCH("a",$D2),0)
However, combining it with the first function doesn't work:
=IFERROR(
INDEX(
B$2:B$11,
SMALL(
IF(
IFERROR(SEARCH("a",$D$2:$D$11),0)>=1,
ROW(B$2:B$11)-ROW(B$2)+1
),
ROWS(B$2:B2)
)
),
""
)
What am I missing from this formula?
There is nothing wrong with the formula IMHO. It worked for me, but I did have problems when I tried to change the original formula in-place with the range selected. If you start off in a new area in the worksheet or just select the top cell in the original range and then pull it down once you've changed it, it's fine. With the range selected, it didn't change the final B2 to B3 etc. as I pulled it down, so just kept getting the first row number repeated.
BTW don't need the ">=1" in the modified part of the formula because any match will give a result >0 which is equivalent to "true"
IFERROR(SEARCH("a",$D$2:$D$11),0)
See also suggestion for changing the array formulae to ordinary ones before changing them
Changing array formulae