Excel Sumifs with Multiple OR Criteria - excel-formula

I'm trying to build a Sumifs formula that uses multiple OR criteria, but also uses cell references in the criteria. I've found on other posts how to use an array constant (ie. Sumifs('Sum Range', 'Criteria Range', {value 1, value 2})) to achieve this, but array constants don't allow for cell references; only static-defined values.
Here's a simplified example my data: https://i.stack.imgur.com/OdfCN.png
I need to build a Sumifs formula in cell 'TABLE B'!C2 which returns the sum of Price values from column 'TABLE A'!C:C where:
('Table A'!A:A = 'TABLE B'!A:A or "ALL") AND ('Table A'!B:B = 'TABLE B'!B:B or "ALL")
This formula should return a value in 'TABLE B'!C2 of:
'TABLE A'!C3 +'TABLE A'!C4 + 'TABLE A'!C5
= $9.00
Thanks!

You would use this:
=SUMPRODUCT((('Table A'!$A$2:$A$5='TABLE B'!A2)+('Table A'!$A$2:$A$5="ALL"))*(('Table A'!$B$2:$B$5='TABLE B'!B2)+('Table A'!$B$2:$B$5="All")),'Table A'!$C$2:$C$5)

Related

using INDIRECT function with dynamic array formula syntax (Excel O365)

I am using the new dynamic array functions introduced in excel in 2018 (e. g. SEQUENCE, UNIQUE etc. functions).
I have a list of cell references that are that are generated dynamically, and would like to apply the INDIRECT function to these list items. A simplified example:
cell A1: =SEQUENCE(5) (results in rows column A values 1,2,3,4,5 as expected)
cell B1: ="A"&A1# (results in rows column B values A1, A2, A3, A4, A5 as expected)
cell C1: =INDIRECT(B1#) this should give me rows in column C values 1,2,3,4,5, but in fact gives me #VALUE ,#VALUE ,#VALUE ,#VALUE ,#VALUE
So the formula properly recognizes the number of rows of the original dynamic array, but for some reason does not dereference the cells properly. The strings seem to be of the proper format - a simple string function such as LEN also works: setting C1 to =LEN(B1#) results in 5 rows of the value 2.
The syntax per se seems to be OK.. for the special case of =SEQUENCE(1) in cell A1 everything works as intended. I tried the R1C1 reference format also, same result
EDIT
Overall I am trying to achieve the following
import a list form a non-Excel data source list is not a dynamic array, it's just a TSV import. I don't now beforehand how many items are in this list, and it can vary a lot
do several different calculations on values of this list.
so far my approach was to use the COUNT function to determine the number of items in the imported list, and then use that to create the second list using SEQUENCE and INDEX to retrieve values.
the problem arises for some calculations where the data contains references to other rows so I have to use indirect addressing to get at that data
The INDIRECT function cannot accept an array for an argument.
In other words:
=INDIRECT({"a1","a2"}) --> #VALUE! | #VALUE!
So you could, for example, refer to each cell in column B as a single cell:
eg:
C1: =INDIRECT(B1)
and fill down.
Depending on how you are using this, you could also use the INDEX function to return an individual element
To return the third element in the array generated by B1#:
=INDIRECT(INDEX(B1#,3))
EDIT:
After reading your comment, and depending on details you have not shared, you may be able to use a variation of the INDEX function.
For example, to return the contents of A1:A5, based on your SEQUENCE function, you can use:
=INDEX($A:$A, SEQUENCE(5))
but exactly how to apply this to your actual situation depends on the details.
As Rosenfeld points out, INDIRECT() does not accept an array as an input. If you need a function that:
"acts" like INDIRECT()
can accept an array as an input
can return an array as an output
Then we can make our own:
Public Function Indirect_a(rng As Range)
Dim arr, i As Long, j As Long
Dim rngc As Long, rngr As Long
rngc = rng.Columns.Count
rngr = rng.Rows.Count
ReDim arr(1 To rngr, 1 To rngc)
For i = 1 To rngc
For j = 1 To rngr
arr(j, i) = Range(rng(j, i).Value)
Next j
Next i
Indirect_a = arr
End Function
and use it like:
Since it creates a "column-compatible" array, it will spill-down dynamically in Excel 365.It can be used in versions of Excel prior to 365, but it must be array-entered into the block it occupies.
You can use the following formula
=BYROW(B1#,LAMBDA(a,INDIRECT(a)))

How can CountIf accept a structured reference column as criteria VBA-wise?

I'm trying to use structured references to the current columns the same as CountIf does for my UDF function. While
=COUNTIF(Data[Team];Overview[Team])
works, my new function
=CONCATENATEIF(Data[Team];Overview[Team];Data[Data])
doesn't work, since the Overview[Team] criteria Range can't be cast to a single value which is [#This Row].
I tried to change the parameter "criteria" As String as well as different methods. Calling
=CONCATENATEIF(Data[Team];Overview[#Team];Data[Data])
with "#" works as intended. But CountIf can handle [#Team], [Team] and normal ranges like [A1:A4]. So how they do it?
Public Function CONCATENATEIF(check_range As Range, criteria As Range, data_range As Range) As Variant
Dim mydic As Object
Dim L As Long
Set mydic = CreateObject("Scripting.Dictionary")
For L = 1 To check_range.Count
If check_range(L) = criteria Then
mydic(L) = data_range(L)
End If
Next
CONCATENATEIF= Join(mydic.items, ", ")
End Function
What cast does criteria need to work like CountIf's criteria? How can i transform the structured Reference [Team] to [#Team] vba-wise, so it selects the same row, where the Formular is used later.
The table for the problem (sadly can't embed images yet)
COUNTIF works due to inferred reference¹.
If you put a bunch of values in column A and then use =INDEX(A:A, , ) (Index(<column_A>, <all_rows>, <all_columns>)) in an unused column to the right of the data then the result will be from the common row in column A. Since you haven't provided a specific row reference where a single cell reference is expected, the associated (or inferred) row is used. This is why COUNTIF works; it is using an inferred reference from the Overview[Team] column to reference a single cell for criteria; e.g. the cell in Overview[Team] that is on the same row as the formula (also known as Overview[#Team]).
The VBA code is not using an inferred reference. It is referencing the whole column of Overview[Team] where it needs a single cell for criteria (e.g. Overview[#Team]).
You could try to artificially parse the column of criteria down to a single cell with something like Application.Caller.Row or you could just use Overview[#Team] as the criteria like it was intended.
¹ I hope I got that term right. I use it so little that I have a hard time remembering the correct term sometimes.

Sum products of named columns

How do I sum all products of the columns in table with another table?
To make it more clear, look at the image attached. I want the column Cost of table TableA to be equal to
=sum([A]*Lookup([[A];[#Headers]]; Parameters[What]; Parameters[Cost]); ....)
And so on for every column of TableA.
I am however pretty much reluctant of doing it manually and trying to come up with formula to make it automatically, so if I add another column I don't have to modify the formula in column Cost
EDIT
What I have come up so far is something like this:
=sum(
[A]*Lookup([[A];[#Headers]]; Parameters[What]; Parameters[Cost]);
[B]*Lookup([[B];[#Headers]]; Parameters[What]; Parameters[Cost]);
[C]*Lookup([[C];[#Headers]]; Parameters[What]; Parameters[Cost])
)
I want to have a formula that will cover new column if I add one. So, let's say I've added a column named NEW, so the formula should automatically pick it up and effectively work like this:
=sum(
[A]*Lookup([[A];[#Headers]]; Parameters[What]; Parameters[Cost]);
[B]*Lookup([[B];[#Headers]]; Parameters[What]; Parameters[Cost]);
[C]*Lookup([[C];[#Headers]]; Parameters[What]; Parameters[Cost]);
[NEW]*Lookup([[NEW];[#Headers]]; Parameters[What]; Parameters[Cost])
)
The Parameters table will of course include a row with value NEW
If you want it to be (50*4+5*100+1*150) for item 1 then you can apply this formulae in cell B3 assuming that the word "cost is in cell B2
INDEX($B$7:$B$9,MATCH($C$2,$A$7:$A$9,0))*$C3+INDEX($B$7:$B$9,MATCH($D$2,$A$7:$A$9,0))*$D3+INDEX($B$7:$B$9,MATCH($E$2,$A$7:$A$9,0))*$E3
If the parameters in tables TableA and Parameters are in the same order, then you can use the following in column Cost:
=SUMPRODUCT(TRANSPOSE(TableA[#[A]:[C]])*Parameters[Cost])
Entered as array formula (i.e. type the formula then instead of pressing Enter, press Ctrl+Shift+Enter)
EDIT:
If the columns in TableA are not ordered, but those in Parameters are, then you can use the following (I'm not 100% sure it always works, but I've been testing a bit and it seems to work fine):
=SUMPRODUCT(TableA[#[A]:[C]]*LOOKUP(TableA[[#Headers],[A]:[C]],Parameters[What],Parameters[Cost]))
Doesn't need to be entered as array formula.
This answer is amended to take account of your statement that the columns of TableA do not necessarily match entries in the rows of the Parameters table.
If you don't mind using macro code, you could define your own function to do the job:
Option Explicit
Public Function TotalCost(rItemCodes As Range, rCostTable As Range, rItemCounts As Range) As Double
'// this function returns the total cost of an item using
'// a lookup into a table of costs from a code, multiplying
'// the item count by the corresponding cost
Dim rItemCount As Range
Dim rCost As Range
Dim rCode As Range
Dim rMatchingCode As Range
'// Define the code list as the first column of the cost table
Dim rCodeList As Range
Set rCodeList = rCostTable.Columns(1)
TotalCost = 0
Dim ix As Integer
'// Loop through every item code
For ix = 1 To rItemCodes.Columns.Count
Set rItemCount = rItemCounts.Cells(1, ix)
Set rCode = rItemCodes.Cells(1, ix)
Set rMatchingCode = Nothing
On Error Resume Next
'// Find the item that matches in the cost list
Set rMatchingCode = rCodeList.Find(rCode.Value, LookAt:=xlWhole)
'// Check it was found, and calculate the additional cost
If Not rMatchingCode Is Nothing Then
Set rCost = rMatchingCode.Cells(1, 2)
TotalCost = TotalCost + rItemCount.Value * rCost.Value
End If
On Error GoTo 0
Next ix
End Function
If you named the table of costs in the parameters table, say, CostTable (range A8:B10 in your example), and the heading row of table A, say, CodeList (range C1:E1 in your example) then the formula in B2 would be =TotalCost(CodeList,CostTable,C2:E2).

Check if a value is in a string

I'm looking for a formula that will check each cell in Col A to see if it is contained anywhere in the cells in range B1:B3, then return a "true" or "false"
Col A Col B
ACAM-18QT-ANN ACAM-50PLD-ANN,ACAM-18QT-ANN,ACAM-72PL-AQU,
ACAM-50PLD-ANN POBIC-BR-SF,
ACPA-BR-SF ACPA-BR-SF,ACPA-TB2-T-MAN,
Use the SUMPRODUCT with SEARCH:
=SUMPRODUCT(ISNUMBER(SEARCH(A1,$B$1:$B$3))*1)>0

Array Formula To Return List Based on Multiple Conditions

I have a spreadsheet that looks like the picture below. I have some formulas that perform counts on this sheet and compare this data to another data source. An example of one of these would be:
=Countifs(A2:A10676,"0",C2:C10676,"OPEN",D2:D10676,"Current")
How would I return the list of loan numbers associated with the count? For instance, the count above returns 3038. I tried the below formula. I selected C2:C3039, typed the formula and hit Shift + Ctrl + Enter:
=If(And(A2:A10676="0",C2:C10676="OPEN",D2:D10676="Current"),B2:B10676,"")
My thought was that this evaluates the logical for each line and builds an array of the values in column B. When I enter this, each cell in the array is blank.
Can someone please explain how to return an array with values based on multiple criteria?
I see two problems with your formula
=If(And(A2:A10676="0",C2:C10676="OPEN",D2:D10676="Current"),B2:B10676,"")
Firstly you can't use AND to return an array - AND (like OR) returns a single result (TRUE or FALSE), so you need either nested IFs or to use * to simulate AND, i.e. either
=IF(A2:A10676=0,IF(C2:C10676="OPEN",IF(D2:D10676="Current",B2:B10676,"")))
....or.....
=IF((A2:A10676=0)*(C2:C10676="OPEN")*(D2:D10676="Current"),B2:B10676,"")
Note: I used 0 without quotes for the first criteria - for COUNTIFS you can use "0" or 0 but here it needs to match the data type - only use quotes if the data is text formatted - I'm assuming that isn't the case
The second problem is that for both of those the resulting array still has 10675 values because it still includes all the blanks for rows when the criteria aren't met.
To get an array of just 3038 values you can use this formula array entered into the correct sized range:
=INDEX(B2:B10676,SMALL(IF(A2:A10676=0,IF(C2:C10676="OPEN",IF(D2:D10676="Current",ROW(B2:B10676)-ROW(B2)+1)),ROW(INDIRECT("1:"&E2))))
Where E2 contains your COUNTIFS formula
Note that this only works to return an array in worksheet range - it won't work to return an array to be used in another function
Here is an approach that uses a "helper column" rather than an array formula:
In E2 enter the formula:
=IF(AND(A2=0,C2="OPEN",D2="Current"),1+MAX($E$1:E1),"")
and copy down (this marks the multiple rows meeting the criteria)
In F2 enter:
=IFERROR(INDEX(B$2:B$24,MATCH(ROWS($1:1),$E$2:$E$24,0)),"")
and copy down.
You can set a data filter:
Dim wrk As Worksheet
Set wrk = ActiveSheet
If Not wrk.AutoFilterMode Then wrk.range("a1").AutoFilter
wrk.range("a1").AutoFilter field:=1, Criteria1:="0"
wrk.range("a1").AutoFilter field:=3, Criteria1:="OPEN"
wrk.range("a1").AutoFilter field:=4, Criteria1:="Current"
Set wrk = Nothing
Of course, you could manually turn on the filters to how you like, too.
Insert four blank rows at the top, copy A5:D6 into A1 and delete B2. DATA > Sort & Filter, Advanced, Copy to another location, List range: A5:D10680, Criteria range: A1:D2, Copy to: F1:I1, check Unique records only, OK.

Resources