Related
I'm out of idea how I could format consecutive same (respectively only even) values in Excel tables without using VBA.
The conditional formatting shall color only consecutive values and only
all 0s or all even values, when there are not more than 2.
A: ID
B: binary
C: counting
1
1
1
2
0
2
3
0
2
4
1
3
5
0
4
6
0
4
7
0
4
8
1
5
9
1
5
I tried to format with: =COUNTIF(C1:C9, C1) < 3, but then it also colors the 1s and C6:C7, eventho there are more than 2.
I also tried =AND( COUNTIF(C1:C9,C1) < 3, ISEVEN(C1:C9) ) but then it colors nothing.
I could replace the 0s with empty cells so I could check ISEMPTY(B1:B9) but it again colors nothing. Using $ to set absolute changes nothing as well.
Formating duplicates also colors triplets, which also doesn't work for me.
=OR(COUNTIF($C$1:$C$9,C1) = 1, COUNTIF($C$1:$C$6,C1) = 2) works so far, but also colors the 1s (uneven).
=AND(OR(COUNTIF($C$1:$C$9,C1) = 1, COUNTIF($C$1:$C$6,C1) = 2), ISEVEN($C$1:$C$9)) doesn't work.
=AND(OR(COUNTIF($C$1:$C$9,C1) = 1, COUNTIF($C$1:$C$6,C1) = 2), $B$1:$B$9 <> 1) doesn't work as well.
My only solution so far is using 2 formating rules:
color =OR(COUNTIF($C$1:$C$9,C1) = 1, COUNTIF($C$1:$C$6,C1) = 2)
do not color =$B$1:$B$9 = 1
but I think it is terrible.
I worked on it for some hours, maybe I'm missing something really obvious.
I'm not allowed to use VBA, therefore this is ot an option.
EDIT: My 2.rule-solution can be simplificed with:
color =COUNTIF($C$1:$C$9,C1) < 3
do not color =$B$1:$B$9 = 1
I'm still confused why combining both doesn't work:
AND(COUNTIF($C$1:$C$9,C1) < 3; $B$1:$B$9 <> 1)
EDIT2: I know why it didn't work. Don't check <>1 with absolute value-range $B$1$:$B$9
Solution: B1 <> 1 then it loops through.
Now combining both works:
=AND( COUNTIF($C$1:$C$9, C1) < 3, B1 <> 1)
I can't see an easy answer for the binary numbers. You have two cases:
(1) Current cell is zero, previous cell is 1, next cell is zero and next cell but one is 1.
(2) Current cell is zero, previous cell is zero, previous cell but one is 1, next cell is 1.
But then the first pair of numbers is a special case because there is no previous cell.
Strictly speaking the last pair of numbers is a special case as well because there is no following cell.
=OR(AND(ROW()=1,B$1=0,B$2=0,B$3=1),AND(ROW()=2,B$1=0,B$2=0,B$3=1),AND(B1=0,B1048576=1,B2=0,B3=1),AND(B1=0,B1048576=0,B1048575=1,B2=1))
where I have used the fact that you are allowed to wrap ranges to the end of the sheet (B1048576) in conditional formatting.
Adding the condition for the case where there there are two zeroes at the end of the range:
=OR(AND(ROW()=1,B$1=0,B$2=0,B$3=1),
AND(ROW()=2,B$1=0,B$2=0,B$3=1),
AND(B1=0,B1048576=1,B2=0,OR(B3=1,B3="")),
AND(B1=0,B1048576=0,B1048575=1,OR(B2=1,B2="")))
Even this could go wrong if there was something in the very last couple of rows of the sheet, so I suppose to be absolutely safe:
=OR(AND(ROW()=1,B$1=0,B$2=0,B$3=1),
AND(ROW()=2,B$1=0,B$2=0,B$3=1),
AND(Row()>1,B1=0,B1048576=1,B2=0,OR(B3=1,B3="")),
AND(Row()>2,B1=0,B1048576=0,B1048575=1,OR(B2=1,B2="")))
Shorter:
=OR(AND(ROW()<=2,B$1+B$2=0,B$3=1),
AND(B1+B2=0,B1048576=1,OR(B3=1,B3="")),
AND(B1+B1048576=0,B1048575=1,OR(B2=1,B2="")))
Not the cleanest wat but it works. You only need to move your data 1 row below, so headers would be in row 2 and data in row 3 for this formula to work:
=IF(AND(B3=B4,B3<>B5),IF(AND(B4=B3,B4<>B2),TRUE,FALSE),IF(AND(B3=B2,B3<>B1),IF(AND(B3=B4,B3<>B5),FALSE,TRUE),FALSE))
How about this approach (Office 365):
=LET(range,B$1:B$9,
s,IFERROR(TRANSPOSE(INDEX(range,ROW()+SEQUENCE(5,,-2))),1),
t,TEXTJOIN("",,(s=INDEX(range,ROW()))*ISEVEN(s)),
IFERROR(SEARCH("0110",t)<4,IFERROR(SEARCH("010",t)=2,FALSE)))
It creates an array s of 5 values starting point is the current row of the range, adding the 2 values above and below. If the value is out of range it will replace the error with a 1.
The array s is checked for being even (TRUE/FALSE, IFERROR created values are uneven) and the values to equal the value of the current row of the range (TRUE/FALSE).
These two booleans are multiplied creating 1 for both values being TRUE, else 0.
These values are joined and checked for 2 consecutive 1's (surrounded by 0) to be found in the 2nd or 3rd position of the range (this would be the case if two even consecutive equal numbers are found),
if it errors it will look if a unique even number is found (1 surrounded by 0 in 2nd position).
PS I'm unable to test if conditional formatting allows you to type the range as B:B instead of B$1:B$9 (working from a mobile) but that would make it more dynamical, because that way you can easily expand the conditional range.
I'm using the following formula to extract the number preceding an "x" from a string (e.g. ##x## where # equals a number 0-9) but with I have other words in the string that have an "x" in them, the formula doesn't work.
Here's the formula:
=IF(ISBLANK(A154),"",IFERROR(IF(VALUE(MID(A154,MIN(FIND({"1","2","3","4","5","6","7","8","9","0"},A154 & "(1,2,3,4,5,6,7,8,9,0")),FIND(INDEX(SET_TERMS,MATCH(1,COUNTIF(A154,"*"&$R$2:$R$5&"*"),0)),A154,1)-MIN(FIND({"1","2","3","4","5","6","7","8","9","0"},A154 & "(1,2,3,4,5,6,7,8,9,0"))))<=1,"",TRIM(MID(A154,MIN(FIND({"1","2","3","4","5","6","7","8","9","0"},A154 & "(1,2,3,4,5,6,7,8,9,0")),FIND(INDEX(SET_TERMS,MATCH(1,COUNTIF(A154,"*"&$R$2:$R$5&"*"),0)),A154,1)-MIN(FIND({"1","2","3","4","5","6","7","8","9","0"},A154 & "(1,2,3,4,5,6,7,8,9,0")))&" sets")),""))
Notes: SET_TERMS ($R$2:$R$5) is a list: rounds, set, sets, x.
Here are examples where the formula works fine:
Skater jumps 3x5 each side RESULT 3 sets
Russian Twist 3x30 seconds RESULT 3 sets
Push-ups 3x max RESULT 3 sets
Y holds 3x30 seconds RESULT 3 sets
Now, here are two examples of the strings that return a blank because Flexion and Extension have "x" in them:
Neck Flexion 3x20 seconds RESULT Blank
Neck Extension 3x20 seconds Result Blank
Any ideas on how to fix this?
Thanks
You just need to be more specific in what you are looking for.
For example, the following will return the digit prior to the x:
=MID(A1,MIN(FIND({0;1;2;3;4;5;6;7;8;9}&"x",A1&"0x1x2x3x4x5x6x7x8x9x")),1)
If you have Windows Excel 2013+ or O365, and you need to deal with multiple digit numbers, the following will extract space-separated "nodes" that have the pattern of ddx, where dd can be any number (including decimals). You can then use string functions to extract just the number.
=FILTERXML("<t><s>" & SUBSTITUTE(A1," ","</s><s>") & "</s></t>","//s[boolean(number(substring-before(.,'x')))]")
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.
I am looking to make a list of heading type 1, sub-heading type 2 and sub-sub-heading type 3, and each subsequent instance of a heading increments in excel. e.g.
Outcome 1
Output 1.1
Activity 1.1.1
Activity 1.1.2
Output 1.2
Activity 1.2.1
Activity 1.2.2
Activity 1.2.3
Outcome 2
Output 2.1
Activity 2.1.1
etc
Here is my formula - getting to be a complicated nested IF statement:
IF([#Column1]="","",
IF([#Column1]="Outcome", "Outcome " & COUNTIF(tbOOA[[#Headers],[Column1]]:[#Column1], [#Column1]),
IF([#Column1]="Output","Output "& COUNTIF(tbOOA[[#Headers],[Column1]]:[#Column1],"Outcome") ***&"."&*** COUNTIF(tbOOA[[#Headers],[Column1]]:[#Column1],[#Column1]),
"Activity " & "serious help")))
In Column 1, choose from a list of 'Outcome', 'Output', or 'Activity'.
In column 2, calculate the appropriate number, e.g. Output 1.2
If the row is empty, then nothing. - Fine
If it is "Outcome", count from the header until current row for the number of instances of "Outcome". - Fine
Else if it is "Output", count the number of "Outcome"s there are. - Fine
This is where it falls apart. Trying to calculate the number after the "." (bold and italic)
I need to count the # of instances of "Output", but then this has to reset to 1 each time there is a new 'Outcome'.
The logic I'm trying to follow is:
(# of "Outputs" from the table header until the current row) minus
(# of "Outputs" from the table header until the last instance of "Outcome")
I've tried several attempts at calculating row number, but everything has been problematic.
The logic is the same for activities, though will complicate the formula even more and I haven't bothered to start on that until I can get level 2 sorted.
Does anyone know of a similar problem/solution?
If you are open to using hidden helper columns, the formulas become much more manageable. Use Column A to hold your "Outcome", "Output", and "Activity" data.
Then use column B to deal with Outcome numbers, column C to deal with Output numbers, and column D to deal with Activity numbers. Merge the final results together in Column E.
In B1, C1, and D1, manually write in the first values (1, 0, and 0).
Then, fill down starting from B2 with the following:
=IF(A2="Outcome",B1+1,B1)
This works by incrementing only if you have found your next Outcome.
Fill down from C2 with the following:
=IF(A2="Outcome",0,IF(A2="Output",C1+1,C1))
This works by incrementing only if you have found your next Output. It resets to 0 if you have a new Outcome.
Then fill down from D2 with
=IF(OR(A3="Outcome",A3="Output"),0,IF(A3="Activity",D2+1,D2))
It's very similar to the prior formula, but resets on an Outcome or an Output.
Finally, in D4, merge it all together with
=B1&IF(C1>0,"."&C1&IF(D1>0,"."&D1,""),"")
& is a string concatenate operation. By checking if the inner values are 0, we only concatenate . and the next number if the next number is non-zero.
I had a similar problem, where I wanted to create the multi-level heading numbers based on the indentation of list of texts. So the numbers must be generated automatically with a user-defined formula (UDF) like below:
For this to work, you must type ="1" in cell A2. The same formula in A3 (below) must be copied down to A4:A14.
=NextLevelNum(A2;IndentLevel(B3))
Function IndentLevel I took from https://professor-excel.com/how-to-return-the-indentation-of-a-cell-in-excel/
Function NextLevelNum I did myself. All code below.
Option Explicit
Public Function IndentLevel(Ref As Range) As Long
Application.Volatile
IndentLevel = Ref.IndentLevel
End Function
Public Function NextLevelNum(prevNumRef As Range, level As Integer) As String
Dim prevNum As String
Dim nums() As String
Dim prevLevel As Integer
prevNum = prevNumRef.Value
nums = Split(prevNum, ".")
prevLevel = UBound(nums) + 1
' Ensure 1 <= level <= prevLevel +1
level = WorksheetFunction.Max(level, 1)
level = WorksheetFunction.Min(level, prevLevel + 1)
ReDim Preserve nums(0 To level - 1)
If level = prevLevel + 1 Then
nums(level - 1) = "1"
Else
nums(level - 1) = CStr(CInt(nums(level - 1)) + 1)
End If
NextLevelNum = Join(nums, ".")
End Function
I have some fields that need to be split up into different cells. They are in the following format:
Numbers on Mission 21 0 21
Numbers on Mission 5 1 6
The desired output would be 4 separate cells. The first would contain the words in the string "Numbers on Mission" and the subsequent cells would have each number, which is determined by a space. So for the first example the numbers to extract would be 21, 0, 21. Each would be in its own cell next to the string value. And for the second: 5, 1, 6.
I tried using a split function but wasn't sure how to target the numbers specifically, and to identify the numbers based on the spaces separating them.
Pertinent to your first case (Numbers on Mission), the simple solution could be as shown below:
Sub SplitCells()
Const RowHeader As String = "Numbers on Mission"
Dim ArrNum As Variant
ArrNum = Split(Replace(Range("A1"), RowHeader, ""), " ")
For i = 1 To UBound(ArrNum)
Cells(1, i + 2) = ArrNum(i)
Next
Cells(1, 2) = RowHeader
End Sub
The same logic is applicable to your second case. Hope this may help.
Unless I'm overlooking something, you may not need VBA at all. Have you tried the "Text to Columns" option? If you select the cell(s) with the information you would like to split up, and go to Data -> Text to Columns. There, you can choose "delimited" and choose a space as a delimiter, which will split your data into multiple cells, split by where the space is.
edit: Just realized that will also split up your string. In that case, when you are in 3rd part of the Text to Columns, choose a destaination cell that isn't the cell with your data. (I.E. if your data is in A1, choose B1 as destination, and it'll put the split info there. Then just combine the text columns with something like =B1&" "&C1&" "&D1)
I was able to properly split the values using the following:
If i.Value Like "*on Mission*" Then
x = Split(i, " ")
For y = 0 To UBound(x)
i.Offset(0, y + 1).Value = x(y)
Next y
End If