1

I thought I could easily sort this issue out but it took me ages to solve just half of it.

I have a table that contains 100 data cells in a row. Data in each cell are either text-only or text and numbers (see layout at bottom).

I need a function that COUNTs how many cells are present in the table that report the value of N2 OR E to be >=37.

Negative
Positive (N2: 23, E: 23)
Negative                      Function answer: 2
Positive (N2: 37, E: 26)
Positive (N2: 31, E: 38)

So far I could only extract each N2 number with a function [=MID(A2,15,FIND(",",A2)-15)] that considers the 15th character, then a second function counts how many extracted numbers (they have been extracted in B row) are >=37, [=COUNTIF(B2:B100, ">=37")] but have not a clue on how to take the E value into account.

In addition, I would like the function to consider cells containing the N2 value OR the E value >=37.

Is there the chance to have one big function that does that? Is there the chance not to rely on KUTools for Excel?

Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
Dam
  • 11
  • 1
  • 1
    You could try this formula: =IF(OR(--MID(A2,FIND("N2:",A2)+4,2)>=37,--MID(A2,FIND("E:",A2)+3,2)>=37),1,0) in column B and for the final count you could use =COUNTIF(B2:B100,1) – Mario Javier Eugenio Estrada Sep 02 '21 at 17:46
  • 1
    @MarioJavierEugenioEstrada Assuming the OP adds basic error handling (#VALUE! is ugly), the second formula reduces to `=SUM(B2:B100)`. – Mark Balhoff Sep 02 '21 at 19:33
  • @MarkBalhoff I agree! It's right everything that you said, but SUM doesn't work with #VALUE and I'm assuming that he doesn't mind to have the VALUE error (since he is already using the MID function without IFERROR). – Mario Javier Eugenio Estrada Sep 02 '21 at 19:38
  • @MarioJavierEugenioEstrada, what if a number is 100+? – JvdV Sep 03 '21 at 06:22
  • @JvdV you could use this formula: =IF(OR(--MID(A2,FIND("N2:",A2)+4,FIND("E:",A2)-FIND("N2:",A2)-6)>=37,--MID(A2,FIND("E:",A2)+3,FIND(")",A2)-FIND("E:",A2)-3)>=37),1,0) – Mario Javier Eugenio Estrada Sep 03 '21 at 14:15
  • Thank you all for your knowledgeable answers. It worked straight forward for me the suggestion by @MarioJavierEugenioEstrada. I used the formula =IF(OR(--MID(A2,FIND("N2:",A2)+4,2)>=37,--MID(A2,FIND("E:",A2)+3,2)>=37),1,0) to read the "ugly looking like" #VALUE! answer relating to the NEGATIVE cells was – Dam Sep 06 '21 at 09:26

2 Answers2

1

If you have the newest version of Excel, you can use FILTERXML after making some minor changes. First concatenate the whole range with CONCAT, then eliminate all ","s and replace ")"s with spaces in the concatenated string.

For example, the below gets you all the instances over 36 (if you only want the number of times, wrap it in a COUNT):

=FILTERXML("<t><s>"&SUBSTITUTE(
SUBSTITUTE(SUBSTITUTE(CONCAT($F$2:$F$7), ")", " "), ",", ""), " ",
 "</s><s>")&"</s></t>", "//s[number()>=37]")

enter image description here

For more info on dealing with strings, see here.

EDIT: Thanks @MarkBalhoff for catching a missing space in the formula and @JvdV for giving another way with =IFERROR(COUNT(FILTERXML("<t><s>"&SUBSTITUTE(TEXTJOIN(" ",,F2:F6)," ","</s><s>")&"</s></t>","//s[translate(.,',','')*1>=37 or translate(following::*[2],')','')*1>=37]")),0)

EDS
  • 2,155
  • 1
  • 6
  • 21
  • 2
    Wouldn't this double count `Positive (N2: 45, E: 51)`? – Mark Balhoff Sep 02 '21 at 19:47
  • 1
    Maybe rather use `=IFERROR(COUNT(FILTERXML(""&SUBSTITUTE(TEXTJOIN(" ",,F2:F6)," ","")&"","//s[translate(.,',','')*1>=37 or translate(following::*[2],')','')*1>=37]")),0)` to counter the previous comment maybe? – JvdV Sep 02 '21 at 21:00
  • @Mark Balhoff and JvdV thanks, can be fixed by substituting a space for the right parenthesis, see edit – EDS Sep 02 '21 at 21:44
  • I don't see how adding a space will fix the problem and a quick test in Excel backs that up. If I put `=COUNT()` in Excel (swapping `CONCATENATE` for `CONCAT` due to older Excel version) against OP sample data, changing 31 to 39 in the last row changes the answer. The root issue is that the function doesn't know which numbers came from the same row. – Mark Balhoff Sep 03 '21 at 04:04
  • @Mark Balhoff weird, check my picture to show it working. In the current picture it is 4, if I change 31 to 39 it's 5. Must be a version issue – EDS Sep 03 '21 at 05:27
  • The problem is that the count on your screenshot should be three and not four, – JvdV Sep 03 '21 at 05:38
  • Ahhh, the asker wants counts rather than instances. Let me adjust – EDS Sep 03 '21 at 06:06
  • Exactly! I believe the proposed formula in my 1st comment would do the trick. It would at least do so for me since when the array returned should be numbers with a trailing comma which Excel turns into numbers (on my end at least). Therefor `COUNT()` won't have an issue unless `FILTERXML()` returns no elements at all and therefor an error. Hence the `IFERROR()`. Will be interesting to see if you can come up with a cleaner solution =). – JvdV Sep 03 '21 at 07:31
  • @JvdV Yes your formula works for me after I replace `TEXTJOIN` (new in Excel 2019) and swap in `COUNTA` since `COUNT` looks for numeric values and the returned values have commas on the end (e.g., _"38,"_). – Mark Balhoff Sep 03 '21 at 16:19
  • @MarkBalhoff, on my Dutch version of Excel (decimal comma) Excel translates the value into a number by itself and therfor `COUNT()` worked. I already had my suspicion it wouldn't do so if yours is a decimal point. However it would be just a small tweak to solve that. – JvdV Sep 03 '21 at 16:43
  • @JvdV perhaps post your answer and I will delete mine. Now that I'm actually aware of what's being asked, I don't think mine is very helpful haha – EDS Sep 03 '21 at 16:53
  • Yours just need some tinkering – JvdV Sep 03 '21 at 17:10
  • @JvdV Ah ok that makes sense! Euler, I do think your answer has value and is really close. If you don't put the effort into adjusting it to the OP's question, perhaps then if JvdV posts his as an answer it would be reasonable for you to just qualify at the beginning of your post that _If you wanted to count **all numbers** >= 37 (as opposed to, per OP, counting the **cells** that contain **at least one** number >= 37), you could use this formula:_ Or something like that. Googlers will reach this question looking for both. – Mark Balhoff Sep 03 '21 at 17:13
  • @Euler'sDisgracedStepchild, if you want to add it to your answer it's completely fine, but I think for decimal point settings my 1st formula won't work, probably `=IFERROR(COUNT(FILTERXML(""&SUBSTITUTE(SUBSTITUTE(TEXTJOIN(" ",,A1:A5)," ",""),",","")&"","//s[.*1>=37 or translate(following::*[2],')','')*1>=37]")),0)` might. – JvdV Sep 04 '21 at 07:50
  • Thank you all for your knowledgeable answers. It worked straight forward for me the suggestion by @MarioJavierEugenioEstrada. I used the formula =IF(OR(--MID(A2,FIND("N2:",A2)+4,2)>=37,--MID(A2,FIND("E:",A2)+3,2)>=37),1,0) to read the "ugly looking like" #VALUE! answer relating to NEGATIVE cells or 0 and 1 when it relates to POSITIVE cells. Then the COUNTIF function to have the sum of all the positive (1) answers. I did not wrap the two formulas into one only but would that be possible? – Dam Sep 06 '21 at 09:38
0

Since you include the python tag and also reference KU-Tools, I assume you have some familiarity with VBA.

You could easily, and flexibly, implement the logic in Excel VBA using regular expressions.

For this function, I allowed three arguments:

  1. The range to search
  2. The threshold for the values
  3. A list of values to look for

In the regex, the pattern looks for the digits that follow either of the strings in "searchFor". Note that, as written, you need to include the colons in the searchFor strings, and that that the strings are case-sensitive. (easily changed)

Option Explicit
Function CountVals(r As Range, Threshold As Long, ParamArray searchFor() As Variant) As Long
    Dim RE As Object, MC As Object, M As Object
    Dim counter As Long
    Dim vSrc As Variant, v As Variant
    Dim sPat As String
    
'read range into variant array for fastest processing
vSrc = r

'create Pattern
sPat = "(?:" & Join(searchFor, "|") & ")\s*(\d+)"

'initialize regex
Set RE = CreateObject("vbscript.regexp")
With RE
    .Global = True
    .ignorecase = False 'or change to true if capitalization not important
    .Pattern = sPat
    counter = 0
    
    'check each string for the values
    For Each v In vSrc
        Set MC = .Execute(v)
            For Each M In MC
                If CLng(M.submatches(0)) >= Threshold Then counter = counter + 1
            Next M
    Next v
    
CountVals = counter
        
End With
    
End Function

enter image description here

Ron Rosenfeld
  • 53,870
  • 7
  • 28
  • 60