Hi there so I have a list of items as follows - where in the Sample 1 column, Blue (I guess parent) is followed by Red (child) and the account numbers in Acct 1 should all be the same as the preceding Blue in that section (until the next Blue is listed with another acct number).
In the example below the 3th and 7th records need to be tagged/identified. Is there any easy way to do this with a formula? I have about 100K line items with this information. Thank you much!!
Sample 1 Acct
Blue 1234
Red 1234
Red 2458
Red 1234
Blue 5768
Red 5768
Red 2589
Red 5768
This is my suggestion. It looks before and after the current cell in column A to find the first cells which don't match in both directions, then takes the range between these cells. If there are at least as many cells in column B that don't match the current acct as do match it in the current range, then it is assumed that the current cell is a mistake (in the case where there are two children and their acct's don't match, then they would both be flagged).
=LET(range,INDEX(A:A,XMATCH(TRUE,A$1:A2<>A2,0,-1)+ROW(A$1)):INDEX(B:B,XMATCH(TRUE,A2:A$10<>A2,0)+ROW()-2),
sample,INDEX(range,0,1),
acct,INDEX(range,0,2),
equal,COUNTIF(acct,B2),
unequal,COUNTIF(acct,"<>"&B2),
unequal>=equal)
Unfortunately this formula doesn't work in conditional formatting, so it would mean putting it in a separate column, dragging it down and filtering on the TRUE values.
I will have a look at modifying the formula to work in CF.
This works in CF but is slow - will need to remove full-column ranges.
=LET(start,XMATCH(TRUE,A$1:A2<>A2,0,-1)+ROW(A$1),end,XMATCH(TRUE,A2:A$10<>A2,0)+ROW()-2,
sample,FILTER(A:A,(ROW(A:A)>=start)*(ROW(A:A)<=end)),
acct,FILTER(B:B,(ROW(B:B)>=start)*(ROW(B:B)<=end)),
equal,SUM(--(acct=B2)),
unequal,SUM(--(acct<>B2)),
unequal>=equal)
This I think is OK
=LET(start,XMATCH(TRUE,A$1:A2<>A2,0,-1)+1,end,XMATCH(TRUE,A2:A$10<>A2,0)+ROW()-ROW(A$1)-1,
seq,SEQUENCE(ROWS(A$1:A$10)),
sample,FILTER(A$1:A$10,(seq>=start)*(seq<=end)),
acct,FILTER(B$1:B$10,(seq>=start)*(seq<=end)),
equal,SUM(--(acct=B2)),
unequal,SUM(--(acct<>B2)),
unequal>=equal)
If I have misunderstood the whole thing and the parent and child is literally labelled as Blue or Red for the whole of sample 1, then you would just need this in conditional formatting:
=AND(A2="Red",B2<>XLOOKUP("Blue",A$1:A2,B$1:B2,,0,-1))
and this to mark each block with an increasing count
=IF(A2="Blue",C1+1,C1)
It's more efficient to do this with a spill formula that calculates the entire column at once.
I took a screenshot to explain the formula, but all that is required is one formula in cell C2.
Here is the unoptimised formula to create a spill column. It creates a bunch of arrays the height of your data and does operations on them to find the incorrect accounts.
=LET(
MultiLevelID, A2:A9,
Account, B2:B9,
sParent, "Blue",
sChild, "Red",
ArrayIndex, SEQUENCE(ROWS(MultiLevelID)),
ArrayBoolParent, ISNUMBER(XMATCH(MultiLevelID, sParent)),
ArrayParentIndex, IF(ArrayBoolParent=TRUE,ArrayIndex,0),
ArrayAllIndexes, XLOOKUP(ArrayIndex,ArrayParentIndex,ArrayParentIndex,,-1),
ArrayCorrectAccount, XLOOKUP(ArrayAllIndexes,ArrayIndex,Account),
ArrayBoolIncorrect, IF(Account=ArrayCorrectAccount,FALSE,TRUE),
ArrayBoolIncorrect
)
ArrayIndex is an array that starts at 1 and keeps counting up for the height of your data. SEQUENCE is for this exact purpose. It will be used as a 'row' reference. Note, these are rows relative to the selected data, not actual excel row numbers.
ArrayBoolParent is an array with TRUE for every parent and FALSE for every child. XMATCH is searching for the parent name, and returns the index if it is found and and error if not. ISNUMBER makes the output TRUE if found and FALSE if not. Using XMATCH lets you search for multiple criteria. For example, if "Light Blue" was also a parent, you could set sParent equal to `{"Blue", "Light Blue"}.
ArrayParentIndex is an array with the index for every parent and 0 for every child. I'm using an interesting application of IF to do this. When IF is given an array as the condition, it does an IF statement for each item in the array. When a return value is also an array, it returns the item relating to the condition instead of the whole array.
ArrayCorrectedIndexes is an array with all children's indexes pointing to their parent. Similar to IF, using an array with XLOOKUP performs the search on all items in the array separately. I'm also using one of the advanced functions of XLOOKUP, which says if a value isn't found, return the next largest value. This means when I search for index 2, it finds nothing and returns the next smallest value, 1.
ArrayCorrectAccount is an array with the parent account automatically assigned to each child. I used XLOOKUP for clarity, but you could also use INDEX(Account,ArrayCorrectedIndexes).
ArrayBoolIncorrect is an array that returns FALSE if the account is correct, and TRUE if not.
If you are looking for a summary of what needs to be fixed, only return the incorrect indexes:
FirstRow, MIN(ROW(MultiLevelID)) - 1,
FILTER(ArrayIndex + FirstRow, ArrayBoolIncorrect <> FALSE)
Lastly, if you want a dynamic table to summarize the errors, use this:
=LET(
MultiLevelID, A2:A9,
Account, B2:B9,
sParent, "Blue",
sChild, "Red",
FirstRow, MIN(ROW(MultiLevelID)) - 1,
ArrayIndex, SEQUENCE(ROWS(MultiLevelID)),
ArrayBoolParent, ISNUMBER(XMATCH(MultiLevelID, sParent)),
ArrayParentIndex, IF(ArrayBoolParent=TRUE,ArrayIndex,0),
ArrayCorrectedIndexes, XLOOKUP(ArrayIndex,ArrayParentIndex,ArrayParentIndex,,-1),
ArrayCorrectAccount, XLOOKUP(ArrayCorrectedIndexes,ArrayIndex,Account),
ArrayBoolIncorrect, IF(Account=ArrayCorrectAccount,FALSE,TRUE),
ArrayInccorrectIndexes, FILTER(ArrayIndex, ArrayBoolIncorrect <> FALSE),
ArrayInccorrectRows, ArrayInccorrectIndexes + FirstRow,
Categories, ArrayInccorrectIndexes,
Body_Instance, SEQUENCE(ROWS(Categories)),
Body_Parent, INDEX(MultiLevelID,INDEX(ArrayCorrectedIndexes, Categories)),
Body_Child, INDEX(MultiLevelID, Categories),
Body_IncorrectAcct, INDEX(Account, Categories),
Body_CorrectAcct, INDEX(ArrayCorrectAccount, Categories),
Body_IncorrectRows, ArrayInccorrectRows,
Total_Rows, COUNT(Categories),
Array_Seq, {1,2,3,4,5,6},
Array_Header, CHOOSE( Array_Seq, "Instance", "Row Location", "Parent", "Child", "Correct Acct", "Incorrect Acct", ),
Array_Body, CHOOSE( Array_Seq, Body_Instance, Body_IncorrectRows, Body_Parent, Body_Child, Body_CorrectAcct, Body_IncorrectAcct, ),
Array_Total, CHOOSE( Array_Seq, "", "", "", "", "Count", Total_Rows),
Range1,Array_Header,
Range2,Array_Body,
Range3,Array_Total,
Rows1,ROWS(Range1), Rows2,ROWS(Range2), Rows3,ROWS(Range3), Cols1,COLUMNS(Range1),
RowIndex, SEQUENCE(Rows1 + Rows2 + Rows3), ColIndex,SEQUENCE(1, Cols1),
RangeTable,IF(
RowIndex<=Rows1,
INDEX(Range1,RowIndex,ColIndex),
IF(RowIndex<=Rows1+Rows2,
INDEX(Range2,RowIndex-Rows1,ColIndex),
INDEX(Range3,RowIndex-Rows1-Rows2,ColIndex)
)),
Return, RangeTable,
Return
)
Here is my data in Excel, starting from column A to D:
Group Class Time Condition
M Q20 1 Good
M P30 2 Poor
N Q20 1 Poor
M Q20 2 Good
M P30 3 Good
N P30 2 Good
I want to count Group and class by two columns Condition and Time to get different values in the following table
Q20M Q20N P30M P30N
2 0 1 1
I have used the following codes to get the above counts
COUNTIFS(A2:A7,"=M",D2:D7, "=Good",C2:C7, ">=1",B2:B7,"=Q20")
COUNTIFS(A2:A7,"=N",D2:D7, "=Good",C2:C7, ">=1",B2:B7,"=Q20")
COUNTIFS(A2:A7,"=M",D2:D7, "=Good",C2:C7, ">=1",B2:B7,"=P30")
COUNTIFS(A2:A7,"=N",D2:D7, "=Good",C2:C7, ">=1",B2:B7,"=P30")
I want to select a single cell, say under this table, to manipulate the Time and to see the counts for each cell. For example, I want to enter a different number of the Times to see how the numbers are changed. The time is now set at>=1.
Any help would be very much appreciated.
If I understand your question, don't hard-code the 1, but use a reference to the cell, e.g. something like this, if your number is in A10.
COUNTIFS(A2:A7,"=M",D2:D7, "=Good",C2:C7, ">="&A10,B2:B7,"=Q20")
Note that you can just use "Q20" and "P30" and drop the =; same for "M" and "Good".
Change this:
COUNTIFS(A2:A7,"=M",D2:D7, "=Good",C2:C7, ">=1",B2:B7,"=Q20")
To this:
COUNTIFS(A2:A7,"=M",D2:D7, "=Good",C2:C7, ">=" & C8 ,B2:B7,"=Q20")
And enter your number in cell C8
I have a problem with my nested if condition as an Excel formula. I know it would be easier by using VBA, but I have to do it this way.
This is my formula, but it returns FALSE:
=IF(D:D="SUPER";IF(AND(AA:AA="0";AA:AA="1");"V";IF(AA:AA="3";"R";"O")))
The D:D column has 3 filters, I have to apply the same formula with each filter.
The AA:AA column has the following conditions:
- if 0 and 1 -> V
- if 3 -> R
- if anything else -> O
I don't know why it doesn't work, but I would appreciate any advice!
This will return R, because there is 3 in there
enter image description here
I think this is more a matter of prioritizing logic flow. It seems that at least a single occurrence of SUPER with 3 supersedes the rest. The next priority would be SUPER with 0 and SUPER with 1 (both must occur). If none of those apply, default to O.
=if(countifs(d:d, "super", aa:aa, 3), "R", if(not(and(countifs(d:d, "super", aa:aa, 0), countifs(d:d, "super", aa:aa, 1))), "O", "V"))
'with semi-colons
=if(countifs(d:d; "super"; aa:aa; 3); "R"; if(not(and(countifs(d:d; "super"; aa:aa; 0); countifs(d:d; "super"; aa:aa; 1))); "O"; "V"))
=IF(COUNTIFS(D:D;"super";AA:AA;3)<>0;"R";IF(COUNTIFS(D:D;"super";AA:AA;1)*COUNTIFS(D:D;"super";AA:AA;0)=0;"O";"V"))
The order of the criteria was wrong, also you generally can't refer to entire columns in the way you did. This formula first checks whether there's any cell with the value 3. Then if there isn't it multiplies the count of 1s and 0s to check whether there are both numbers present.
Say I have columns
/670 - White | /650 - black | /680 - Red | /800 - Whitest
These have data in their rows. Basically, I want to SUM their values together if their headers contain my desired string.
For modularity's sake, I wanted to merely specify to sum /670, /650, and /680 without having to mention the rest of the header text.
So, something like =SUMIF(a1:c1; "/NUM & /NUM & /NUM"; a2:c2)
That doesn't work, and honestly I don't know what i should be looking for.
Additional stuff:
I'm trying to think of the answer myself, is it possible to mention the header text as condition for ifs? Like: if A2="/650 - Black" then proceed to sum the next header. Is this possible?
Possibility it would not involve VBA, a draggable formula would be preferable!
At this point, I may as well request a version which handles the complete header name rather than just a part of it as I believe it to be difficult for formula code alone.
Thanks for having a look!
Let me know if I need to elaborate.
EDIT: In regards to data samples, any positive number will do actually, damn shame stack overflow doesn't support table markdown. Anyway, for example then..:
+-------------+-------------+-------------+-------------+-------------+
| A | B | C | D | E |
+---+-------------+-------------+-------------+-------------+-------------+
| 1 |/650 - Black |/670 - White |/800 - White |/680 - Red |/650 - Black |
+---+-------------+-------------+-------------+-------------+-------------+
| 2 | 250 | 400 | 100 | 300 | 125 |
+---+-------------+-------------+-------------+-------------+-------------+
I should have clarified:
The number range for these headers would go from /100 - /9999 and no more than that.
EDIT:
Progress so far:
https://docs.google.com/spreadsheets/d/1GiJKFcPWzG5bDsNt93eG7WS_M5uuVk9cvkt2VGSbpxY/edit?usp=sharing
Formula:
=SUMPRODUCT((A2:D2*
(MID($A$1:$D$1,2,4)=IF(LEN($H$1)=4,$H$1&"",$H$1&" ")))+(A2:D2*
(MID($A$1:$D$1,2,4)=IF(LEN($I$1)=4,$I$1&"",$I$1&" ")))+(A2:D2*
(MID($A$1:$D$1,2,4)=IF(LEN($J$1)=4,$J$1&"",$J$1&" "))))
Apparently, each MID function is returning false with each F9 calculation.
EDIT EDIT:
Okay! I found my issue, it's the /being read when you ALSO mentioned that it wasn't required. Man, I should stop skimming!
Final Edit:
=SUMPRODUCT((RETURNSUM*
(MID(HEADER,2,4)=IF(LEN(Match5)=4,Match5&"",Match5&" ")))+(RETURNSUM*
(MID(HEADER,2,4)=IF(LEN(Match6)=4,Match6&"",Match6&" ")))+(RETURNSUM*
(MID(HEADER,2,4)=IF(LEN(Match7)=4,Match7&"",Match7&" ")))
The idea is that Header and RETURNSUM will become match criteria like the matches written above, that way it would be easier to punch new criterion into the search table. As of the moment, it doesn't support multiple rows/dragging.
I have knocked up a couple of formulas that will achieve what you are looking for. For ease I have made the search input require the number only as pressing / does not automatically type into the formula bar. I apologise for the length of the answer, I got a little carried away with the explanation.
I have set this up for 3 criteria located in J1, K1 and L1.
Here is the output I achieved:
Formula 1 - SUMPRODUCT():
=SUMPRODUCT((A4:G4*(MID($A$1:$G$1,2,4)=IF(LEN($J$1)=4,$J$1&"",$J$1&" ")))+(A4:G4*(MID($A$1:$G$1,2,4)=IF(LEN($K$1)=4,$K$1&"",$K$1&" ")))+(A4:G4*(MID($A$1:$G$1,2,4)=IF(LEN($L$1)=4,$L$1&"",$L$1&" "))))
Sumproduct(array1,[array2]) behaves as an array formula without needed to be entered as one. Array formulas break down ranges and calculate them cell by cell (in this example we are using single rows so the formula will assess columns seperately).
(A4:G4*(MID($A$1:$G$1,2,4)=IF(LEN($J$1)=4,$J$1&"",$J$1&" ")))
Essentially I have broken the Sumproduct() formula into 3 identical parts - 1 for each search condition. (A4:G4*: Now, as the formula behaves like an array, we will multiply each individual cell by either 1 or 0 and add the results together.
1 is produced when the next part of the formula is true and 0 for when it is false (default numeric values for TRUE/FALSE).
(MID($A$1:$G$1,2,4)=IF(LEN($J$1)=4,$J$1&"",$J$1&" "))
MID(text,start_num,num_chars) is being used here to assess the 4 digits after the "/" and see whether they match with the number in the 3 cells that we are searching from (in this case the first one: J1). Again, as SUMPRODUCT() works very much like an array formula, each cell in the range will be assessed individually.
I have then used the IF(logical_test,[value_if_true],[value_if_false]) to check the length of the number that I am searching. As we are searching for a 4 digit text string, if the number is 4 digits then add nothing ("") to force it to a text string and if it is not (as it will have to be 3 digits) add 1 space to the end (" ") again forcing it to become a text string.
The formula will then perform the calculation like so:
The MID() formula produces the array: {"650 ","670 ","800 ","680 ","977 ","9999","143 "}. This combined with the first search produces {TRUE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE} which when multiplied by A4:G4
(remember 0 for false and 1 for true) produces this array: {250,0,0,0,0,0,0} essentially pulling the desired result ready to be summed together.
Formula 2: =SUM(IF(Array)): [This formula does not work for 3 digit numbers as they will exist within the 4 digit numbers! I have included it for educational purposes only]
=SUM(IF(ISNUMBER(SEARCH($J$1,$A$1:$G$1)),A8:G8),IF(ISNUMBER(SEARCH($K$1,$A$1:$G$1)),A8:G8),IF(ISNUMBER(SEARCH($L$1,$A$1:$G$1)),A8:G8))
The formula will need to be entered as an array (once copy and pasted while still in the formula bar hit CTRL+SHIFT+ENTER)
This formula works in a similar way, SUM() will add together the array values produced where IF(ISNUMBER(SEARCH() columns match the result column.
SEARCH() will return a number when it finds the exact characters in a cell which represents it's position in number of characters. By using ISNUMBER() I am avoiding having to do the whole MID() and IF(LEN()=4,""," ") I used in the previous formula as TRUE/FALSE will be produced when a match is found regardless of it's position or cell formatting.
As previously mentioned, this poses a problem as 999 can be found within 9999 etc.
The resulting array for the first part is: {250,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE} (if you would like to see the array you can highlight that part of the formula and calculate with F9 but be sure to highlight the exact brackets for that part of the formula).
I hope I have explained this well, feel free to ask any questions about stuff that you don't understand. It is good to see people keen to learn and not just fishing for a fast answer. I would be more than happy to help and explain in more depth.
I start this solution with the names in an array, you can read the header names into an array with not too much difficulty.
Sub test()
Dim myArray(1 To 4) As String
myArray(1) = "/670 - White"
myArray(2) = "/650 - black"
myArray(3) = "/680 - Red"
myArray(4) = "/800 - Whitest"
For Each ArrayValue In myArray
'Find position of last character
endposition = InStr(1, ArrayValue, " - ", vbTextCompare)
'Grab the number section from the string, based on starting and ending positions
stringvalue = Mid(ArrayValue, 2, endposition - 2)
'Convert to number
NumberValue = CLng(stringvalue)
'Add to total
Total = Total + NumberValue
Next ArrayValue
'Print total
Debug.Print Total
End Sub
This will print the answer to the debug window.