Count Patterns In one Cell Excel - excel

I wanted your help, I'm currently working in extracting some data, now the thing is that I have to count an specific amount of Call IDs a call ID format is the following 9129572520020000711. The pattern is 19 characters that starts with 9 and ends in 1.
and I want to count how many times this pattern appears in one cell
I.E. this is the value in one cell and I want to count how many times the pattern appears.
1912957252002000071129129545183410000711391295381628700007114912959791875000071159129597085000000711691295892838400007117912958908933000071189129452513730000711

To solve this with formulae you need to know:
The starting character
The ending character
The length of your Call ID
Finding all possible Call IDs
Let B1 be your number string and B2 be the call ID (or pattern) you are looking for. In B5 enter the formula =MID($B$2,1,1) to find the starting character you are looking for. In B6 enter =RIGHT($B$2,1) for the end character. In B7 enter =LEN($B$2) for the length of the call ID.
In Column A we'll enter the position of every starting character. The first formula will be a simple Find() formula in B10 as =FIND($B$5,$B$1,1). To find the other starting characters start the Find() at the location after the last starting character: =FIND($B$5,$B$1,$A10+1) in B11. Copy this down the column a few dozen times (or more).
In Column B we'll see if the next X characters (where X is the length of the Call ID) meets the criteria for a Call ID:
=IF(MID($B$1,$A10+($B$7-1),1)=$B$6,TRUE,FALSE)
The MID($B$1,$A10+($B$7-1),1)=$B$6 checks if the character at the end of the character at the end of this possible Call ID is the end character we're looking for. $A10+($B$7) calculates the position of the possible Call ID and $B$6 is the end character.
In Column C we can return the actual Call ID if there is a match. This isn't necessary to find the count, but will be useful later. Simply check if the value in Column B is True and, if yes, return the calculated string: =IF(B10,MID($B$1,$A10,$B$7),"").
To actually count the number of valid Call IDs, do a CountIf() of the Call ID column to check for the number of True values: =IF(B10,MID($B$1,$A10,$B$7),"").
If you don't want all the #Values! just wrap everything in IFERROR(,"") formulas.
Finding all consecutive Call IDs
However , some of these Call IDs overlap. Operating on the assumption that Call IDs cannot overlap, we simply have to start our search after the end character of a found ID, not the start. Insert an "Ending Position" column in Column B with the formulae: =$A10+($C$7-1), starting in B11. Alter A11 to =FIND($C$5,$C$1,$B10+1) and copy down. Don't change A10 as this finds the first starting position and is not depending on anything but the original text.
Which ones are valid?
I don't know, that depends on other criteria for your Call IDs. If you receive them consecutively, then the second method is best and the other possible ones found are by coincidence. If not, then you'll have to apply some other validation criteria to the first method, hence why we identified each ID.

You can solve this simply with a UDF using a regular expression.
Option Explicit
Function callIDcount(S As String) As Long
Dim RE As Object, MC As Object
Const sPat As String = "9\d{17}1"
Set RE = CreateObject("vbscript.regexp")
With RE
.Global = True
.Pattern = sPat
Set MC = .Execute(S)
callIDcount = MC.Count
End With
End Function
Using your example, this returns a count of 8
The regular expression engine captures all of the matches that match the pattern, into the match collection. To see how many are there, we merely return the count of that collection.
Trivial modifications would allow one to return the actual ID's also, should that be necessary.
The regex:
9\d{17}1
9\d{17}1
Match the character “9” literally 9
Match a single character that is a “digit” (ASCII 0–9 only) \d{17}
Exactly 17 times {17}
Match the character “1” literally 1
Created with RegexBuddy
EDIT Reading through TheFizh's post, he considered that you might want the count to include overlapping CallID's. In other words, given:
9129572520020000711291
We see that includes:
9129572520020000711
9572520020000711291
where the second overlaps with the first, but both meet your requirements.
Should that be what you want, merely change the regex so it does not "consume" the match:
Const sPat As String = "9(?=\d{17}1)"
and you will return the result of 15 instead of 8, which would be non-overlapping pattern.

Do you mean something like what's following?
Sub CallID_noPatterns()
Dim CallID As String, CallIDLen As Integer
CallID = "9#################1"
CallIDLen = Len(CallID) 'the CallID's length
'Say that you want to get the value of "A1" cell and deal with its value
Dim CellVal As String, CellLen As Integer
CellVal = CStr(Range("A1").Text) 'get its value as a string
CellLen = Len(CellVal) 'get its length
'You Have 2 options:-
'1-The value is smaller than your CallID length. (Not Applicable)
'2-The value is longer than or equal to your CallID length
'So just run your code for the 2nd option
Dim i As Integer, num_checks, num_patterns
i = 0
num_patterns = 0
'imagine both of them as 2 arrays, every array consists of sequenced elements
'and your job is to take a sub-array from your value, of a length
' equals to CallID's length
'then compare your sub-array with CallID
num_checks = CellLen - CallIDLen + 1
If CellLen >= CallIDLen Then
For i = 0 To num_checks - 1 Step 19
For j = i To num_checks - 1
If Mid(CellVal, (j + 1), CallIDLen) Like CallID Then
num_patterns = num_patterns + 1
Exit For
End If
Next j
Next i
End If
'Display your result
MsgBox "Number of Patterns: " & Str(num_patterns)
End Sub

Related

Extracting a substring from a cell at a certain index (different row length, multiple rows)

Working as an accountant, and I'm wondering if there's a way to extract a certain string of characters, at a certain index, from a cell of varying lengths. Sometimes, I receive a statement of account as a PDF, which doesn't convert nicely to an Excel spreadsheet.
This is what I want to achieve
The strings are at the same index within each cell. I can't quite figure out how to isolate them, though, because each string is a different length, and the substring I want doesn't start with the same character. I've tried LEN, MID, etc. to extract them, but I'm not sure how to do it.
I was able to extract items that start with specific characters (like INV and "45") with:
=TRIM(MID(SUBSTITUTE(A1," ",REPT(" ",99)),MAX(1,FIND("XXX",SUBSTITUTE(A1," ",REPT(" ",99)))-50),99))
But I can't figure out how to get the strings I'm looking for (in this case, the amounts for the invoices).
If you don't mind some VBA, you can try this:
The basic problem with the data is the dates included in the fields are variable length and include the only real delimiter (spaces) within those elements. Using the spaces from the right, can be used to return your fields.
Credit to Sumit Bansal at (https://trumpexcel.com) for the original LastPosition() function. However, this function only returns the last occurrence of a character.
But a couple of slight modifications, LastPositionN() now returns the nth occurrence of the search text, to be specified as a 3rd parameter.
Note: Follow here for the original function, and info on cutting/pasting into the VBA editor: https://trumpexcel.com/find-characters-last-position/
Function LastPosition(rCell As Range, rChar As String)
'This function gives the last nth position of the specified character
'This code has been developed by Sumit Bansal (https://trumpexcel.com)
Dim rLen As Integer
rLen = Len(rCell)
For i = rLen To 1 Step -1
If Mid(rCell, i - 1, 1) = rChar Then
'LastPosition = i ' original code returnd Last position +1
LastPosition = i - 1 ' correction returns codes original intent
Exit Function
End If
Next i
End Function
Function LastPositionN(rCell As Range, rChar As String, Optional nNthOccurance As Integer = 1)
'This function gives the last position of the specified character
'This code has been developed by Sumit Bansal
' (https://trumpexcel.com)
' and was modified my Jeff Bowman 9/11/2018 for nth position from right
'Returns zero (0) if not nth occurence is not found
Dim rLen As Integer, nNthCount As Integer, LastPosition As Integer
nNthCount = 0
rLen = Len(rCell)
For i = rLen To 1 Step -1
If Mid(rCell, i - 1, 1) = rChar Then
'LastPosition = i ' original code returnd Last position +1
LastPositionN = i - 1 ' correction returns codes original
'intent
nNthCount = nNthCount + 1
If nNthCount = nNthOccurance Then
Exit Function
End If
End If
Next i
End Function
`
Now, with left(), right(), and find() we can isolate and parse the end of the data string.
=LEFT(RIGHT(A8,LastPositionN(A8," ",1)),FIND(" ",RIGHT(A8,LastPositionN(A8," ",1))))

Differentiate full stop from decimal values

I'm working on a formula that checks a cell for a "£" sign, then checks the value after it. If the value is anything other than a rounded value, e.g. "£30" or "£30.00", it would be an error.
My problem is, the text has a full stop after the decimal value. This results in the 'DECIMAL' value being returned. How do I not count £30. as an error.
Below is the formula as input through VBA
=IF(ISERROR(SEARCH(""."",LEFT((RIGHT(RC[-2],LEN(RC[-2])-SEARCH(""£"",RC[-2]))),SEARCH("" "",(RIGHT(RC[-2],LEN(RC[-2])-SEARCH(""£"",RC[-2])))))))=FALSE,IF(ISERROR(SEARCH("".00"",LEFT((RIGHT(RC[-2],LEN(RC[-2])-SEARCH(""£"",RC[-2]))),SEARCH("" "",(RIGHT(RC[-2],LEN(RC[-2])-SEARCH(""£"",RC[-2])))))))=FALSE,"""",""DECIMAL""),"""")
Here is a User Defined Function that should do what you want. It looks for a substring that starts with the € currency sign and then some number of digits. If there is a dot following, it looks at the dot and the next two digits (or nothing if no digits). If the value of that sequence is zero, then the function returns TRUE, else FALSE
Obviously, you can modify this to return whatever you want, or use it within an IF formula on the worksheet.
Note that it will only work on strings, and not on values formatted as currency.
Option Explicit
Function ValidCurrency(S As String) As Boolean
Dim RE As Object, MC As Object
Const sPattern As String = "€\s*\d+(\.\d{2})?"
ValidCurrency = False
Set RE = CreateObject("vbscript.regexp")
With RE
.Pattern = sPattern
If .test(S) = True Then
Set MC = .Execute(S)
ValidCurrency = (MC(0).submatches(0) = 0)
End If
End With
End Function
Assuming the string you want to check is in F1, I think you could do something like this:
=IF(ISNUMBER(FIND("£",F1)),IF(TRIM(MID(F1&"00",FIND("£",F1)+1,5))="30.00",TRUE,FALSE),FALSE)
This:
assumes the £ number you want to inspect is the first occurrence of that "£" character in the string.
Assumes that an occurrence of "£30." would occur at the end of the sentence, as you assert in your comments above.
But perhaps you left out some details for examples in which it won't work.
For your follow up question, the following would work for all two-digit whole number values (10 thru 99). Note that I'm comparing the value in the string to its ROUND()ed value to see if the two match, and thus make sure there's nothing after the decimal. This should be enough for you to adapt it further on your own if you need to cover a broader range of values, but at this point, I think I've answered your original question and then some.
=IF(ISNUMBER(FIND("£",F1)),IF(VALUE(TRIM(MID(F1&"00",FIND("£",F1)+1,5)))=ROUND(VALUE(TRIM(MID(F1&"00",FIND("£",F1)+1,5))),0),TRUE,FALSE),FALSE)

Excel - Checking if 2 strings match, if they don't, return the position of the string where the two diverge

I have two strings that I want to compare. The function I want is to basically:
(1) Check if the two strings match exactly
(2) If they do match, return TRUE
(3) If they do not match, return the position of the string where the two diverge
For example:
Cell A1: Barack Obama
Cell A2: Barack Obana
I know that these two strings don't match and the error is the "n" in "Obana". Therefore, the error happens at the string position of 10 in A2. I would like the function to return 10.
My attempt:
=IF(EXACT(A1,A2), "MATCH", ??(SEARCH(A1,A2,1))??
Thanks!
How about the following VBA function:
Function MatchOrDiverge(BaseString As String, ComparedString As String)
If BaseString = ComparedString Then
MatchOrDiverge = "MATCH"
Else
For i = 1 To Len(BaseString)
If Not (Mid(BaseString, i, 1) = Mid(ComparedString, i, 1)) Then
MatchOrDiverge = i
Exit Function
End If
Next i
MatchOrDiverge = Len(BaseString) + 1
End If
End Function
This takes 2 strings as input. First, it checks to see if the 2 strings are the same. If they are, it returns "MATCH".
If the 2 strings are not equal, it loops through the BaseString and checks its characters against the ComparedString. When a character does not match, it returns that character's index.
If the strings match, but the second one is longer (e.g., "cat" and "cattle"), then it returns the length of the BaseString + 1.
Screenshot in action:
Here is a formula:
=IF(EXACT(A1,A2),"MATCH",AGGREGATE(15,6,ROW(INDIRECT("1:" & MAX(LEN(A1),LEN(A2))))/(NOT(EXACT(MID(A1,ROW(INDIRECT("1:" & MAX(LEN(A1),LEN(A2)))),1),MID(A2,ROW(INDIRECT("1:" & MAX(LEN(A1),LEN(A2)))),1)))),1))
This is a long and convoluted formula and changing the references is not quick. The UDF option given by elmer007 would be easier to use and reference in the long run.

How to count up elements in excel

So I have a column called chemical formula for like 40,000 entries, and what I want to be able to do is count up how many elements are contained in the chemical formula. So for example:-
EXACT_MASS FORMULA
626.491026 C40H66O5
275.173274 C13H25NO5
For this, I need some kind of formula that will return with the result of
C H O
40 66 5
13 25 5
all as separate columns for the different elements and in rows for the different entries. Is there a formula that can do this?
You could make your own formula.
Open the VBA editor with ALT and F11 and insert a new module.
Add a reference to Microsoft VBScript Regular Expressions 5.5 by clicking Tools, then references.
Now add the following code:
Public Function FormulaSplit(theFormula As String, theLetter As String) As String
Dim RE As Object
Set RE = CreateObject("VBScript.RegExp")
With RE
.Global = True
.MultiLine = False
.IgnoreCase = False
.Pattern = "[A-Z]{1}[a-z]?"
End With
Dim Matches As Object
Set Matches = RE.Execute(theFormula)
Dim TheCollection As Collection
Set TheCollection = New Collection
Dim i As Integer
Dim Match As Object
For i = (Matches.Count - 1) To 0 Step -1
Set Match = Matches.Item(i)
TheCollection.Add Mid(theFormula, Match.FirstIndex + (Len(Match.Value) + 1)), UCase(Trim(Match.Value))
theFormula = Left(theFormula, Match.FirstIndex)
Next
FormulaSplit = "Not found"
On Error Resume Next
FormulaSplit = TheCollection.Item(UCase(Trim(theLetter)))
On Error GoTo 0
If FormulaSplit = "" Then
FormulaSplit = "1"
End If
Set RE = Nothing
Set Matches = Nothing
Set Match = Nothing
Set TheCollection = Nothing
End Function
Usage:
FormulaSplit("C40H66O5", "H") would return 66.
FormulaSplit("C40H66O5", "O") would return 5.
FormulaSplit("C40H66O5", "blah") would return "Not found".
You can use this formula directly in your workbook.
I've had a stab at doing this in a formula nad come up with the following:
=IFERROR((MID($C18,FIND(D17,$C18)+1,2))*1,IFERROR((MID($C18,FIND(D17,$C18)+1,1))*1,IFERROR(IF(FIND(D17,$C18)>0,1),0)))
It's not very neat and would have to be expanded further if any of your elements are going to appear more than 99 times - I also used a random placement on my worksheet so the titles H,C and O are in row 17. I would personally go with Jamie's answer but just wanted to try this to see if I could do it in a formula possible and figured it was worth sharing just as another perspective.
Even though this has an excellent (and accepted) VBA solution, I couldn't resist the challenge to do this without using VBA.
I posted a solution earlier, which wouldn't work in all cases. This new code should always work:
=MAX(
IFERROR(IF(FIND(C$1&ROW($1:$99),$B2),ROW($1:$99),0),0),
IFERROR(IF(FIND(C$1&CHAR(ROW($65:$90)),$B2&"Z"),1,0),0)
)
Enter as an array formula: Ctrl + Shift + Enter
Output:
The formula outputs 0 when not found, and I simply used conditional formatting to turn zeroes gray.
How it works
This part of the formula looks for the element, followed by a number between 1 and 99. If found, the number of atoms is returned. Otherwise, 0 is returned. The results are stored in an array:
IFERROR(IF(FIND(C$1&ROW($1:$99),$B2),ROW($1:$99),0),0)
In the case of C13H25NO5, a search for "C" returns this array:
{1,0,0,0,0,0,0,0,0,0,0,0,13,0,0,0,...,0}
1 is the first array element, because C1 is a match. 13 is the thirteenth array element, and that's what we're interested in.
The next part of the formula looks for the element, followed by an uppercase letter, which indicates a new element. (The letters A through Z are characters 65 through 90.) If found, the number 1 is returned. Otherwise, 0 is returned. The results are stored in an array:
IFERROR(IF(FIND(C$1&CHAR(ROW($65:$90)),$B2&"Z"),1,0),0)
"Z" is appended to the chemical formula, so that a match will be found when its last element has no number. (For example, "H2O".) There is no element "Z" in the Periodic Table, so this won't cause a problem.
In the case of C13H25NO5, a search for "N" returns this array:
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0}
1 is the 15th element in the array. That's because it found the letters "NO", and O is the 15th letter of the alphabet.
Taking the maximum value from each array gives us the number of atoms as desired.

Deleting variable number of leading characters from a variable-length string

If I am having G4ED7883666 and I want the output to be 7883666
and I have to apply this on a range of cells and they are not the same length and the only common thing is that I have to delete anything before the number that lies before the alphabet?
This formula finds the last number in a string, that is, all digits to the right of the last alpha character in the string.
=RIGHT(A1,MATCH(99,IFERROR(1*MID(A1,LEN(A1)+1-ROW($1:$25),1),99),0)-1)
Note that this is an array formula and must be entered with the Control-Shift-Enter keyboard combination.
How the formula works
Let's assume that the target string is fairly simple: "G4E78"
Working outward from the middle of the formula, the first thing to do is create an array with the elements 1 through 25. (Although this might seem to limit the formula to strings with no more than 25 characters, it actually places a limit of 25 digits on the size of the number that may be extracted by the formula.
ROW($1:$25) = {1;2;3;4;5;6;7; etc.}
Subtracting from this array the value of (1 + the length of the target string) produces a new array, the elements of which count down from the length of string. The first five elements will correspond to the position of the characters of the string - in reverse order!
LEN(A1)+1-ROW($1:$25) = {5;4;3;2;1;0;-1;-2;-3;-4; etc.}
The MID function then creates a new array that reverses the order of the characters of the string.
For example, the first element of the new array is the result of MID(A1, 5, 1), the second of MID(A1, 4, 1) and so on. The #VALUE! errors reflect the fact that MID cannot evaluate 0 or negative values as the position of a string, e.g., MID(A1,0,1) = #VALUE!.
MID(A1,LEN(A1)+1-ROW($1:$25),1) = {"8";"7";"E";"4";"G";#VALUE!;#VALUE!; etc.}
Multiplying the elements of the array by 1 turns the character elements of that array to #VALUE! errors as well.
=1*MID(A1,LEN(A1)+1-ROW($1:$25),1) = {"8";"7";#VALUE!;"4";#VALUE!;#VALUE!;#VALUE!; etc.}
And the IFERROR function turns the #VALUES into 99, which is just an arbitrary number greater than the value of a single digit.
IFERROR(1*MID(A1,LEN(A1)+1-ROW($1:$25),1),99) = {8;7;99;4;99;99;99; etc.}
Matching on the 99 gives the position of the first non-digit character counting from the right end of the string. In this case, "E" is the first non-digit in the reversed string "87E4G", at position 3. This is equivalent to saying that the number we are looking for at the end of the string, plus the "E", is 3 characters long.
MATCH(99,IFERROR(1*MID(A1,LEN(A1)+1-ROW($1:$25),1),99),0) = 3
So, for the final step, we take 3 - 1 (for the "E) characters from the right of string.
RIGHT(A1,MATCH(99,IFERROR(1*MID(A1,LEN(A1)+1-ROW($1:$25),1),99),0)-1) = "78"
One more submission for you to consider. This VBA function will get the right most digits before the first non-numeric character
Public Function GetRightNumbers(str As String)
Dim i As Integer
For i = Len(str) To 0 Step -1
If Not IsNumeric(Mid(str, i, 1)) Then
Exit For
End If
Next i
GetRightNumbers = Mid(str, i + 1)
End Function
You can write some VBA to format the data (just starting at the end and working back until you hit a non-number.)
Or you could (if you're happy to get an addin like Excelicious) then you can use regular expressions to format the text via a formula. An expression like [0-9]+$ would return all the numbers at the end of a string IIRC.
NOTE: This uses the regex pattern in James Snell's answer, so please upvote his answer if you find this useful.
Your best bet is to use a regular expression. You need to set a reference to VBScript Regular Expressions for this to work. Tools --> References...
Now you can use regex in your VBA.
This will find the numbers at the end of each cell. I am placing the result next to the original so that you can verify it is working the way you want. You can modify it to replace the cell as soon as you feel comfortable with it. The code works regardless of the length of the string you are evaluating, and will skip the cell if it doesn't find a match.
Sub GetTrailingNumbers()
Dim ws As Worksheet
Dim rng As Range
Dim cell As Range
Dim result As Object, results As Object
Dim regEx As New VBScript_RegExp_55.RegExp
Set ws = ThisWorkbook.Sheets("Sheet1")
' range is hard-coded here, but you can define
' it programatically based on the shape of your data
Set rng = ws.Range("A1:A3")
' pattern from James Snell's answer
regEx.Pattern = "[0-9]+$"
For Each cell In rng
If regEx.Test(cell.Value) Then
Set results = regEx.Execute(cell.Value)
For Each result In results
cell.Offset(, 1).Value = result.Value
Next result
End If
Next cell
End Sub
Takes the first 4 digits from the right of num:
num1=Right(num,4)
Takes the first 5 digits from the left of num:
num1=Left(num,5)
First takes the first ten digits from the left then takes the first four digits from the right:
num1=Right(Left(num, 10),4)
In your case:
num=G4ED7883666
num1=Right(num,7)

Resources