Extract maximum number from a string - string

I am trying to extract all numbers from a string with a function in Excel.
In the second time, I would like to extract the maximum value contains in the string.
My string look likes:
ATCG=12.5,TTA=2.5,TGC=60.28
Desired output: 60.28
In a first time, I am trying to extract all numbers with my function but it stops only on the first figure.
Function MyCode(ByVal txt As String) As String
With CreateObject("VBScript.RegExp")
.Pattern = "\d.+"
If .test(txt) Then MyCode = .Execute(txt)(0)
End With
End Function

Here is some VBA (not vbscript) that you can adapt to you needs:
Public Function MyCode(ByVal txt As String) As String
Dim maxi As Double, db As Double
maxi = -9999
arr = Split(Replace(txt, "=", ","), ",")
For Each a In arr
If IsNumeric(a) Then
db = CDbl(a)
If db > maxi Then maxi = db
End If
Next a
MyCode = CStr(maxi)
End Function
NOTE:
This gives a String and not a Number.
EDIT#1:
In Excel-VBA, the code must be placed in a standard module.
User Defined Functions (UDFs) are very easy to install and use:
ALT-F11 brings up the VBE window
ALT-I
ALT-M opens a fresh module
paste the stuff in and close the VBE window
If you save the workbook, the UDF will be saved with it.
If you are using a version of Excel later then 2003, you must save
the file as .xlsm rather than .xlsx
To remove the UDF:
bring up the VBE window as above
clear the code out
close the VBE window
To use the UDF from Excel:
=MyCode(A1)
To learn more about macros in general, see:
http://www.mvps.org/dmcritchie/excel/getstarted.htm
and
http://msdn.microsoft.com/en-us/library/ee814735(v=office.14).aspx
and for specifics on UDFs, see:
http://www.cpearson.com/excel/WritingFunctionsInVBA.aspx
Macros must be enabled for this to work!

You don't really need VBA for this if you have a version of Excel (2010+) that includes the AGGREGATE function, you can do it with a worksheet formula:
=AGGREGATE(14,6,--TRIM(MID(SUBSTITUTE(SUBSTITUTE(A1,",",REPT(" ",99)),"=",REPT(" ",99)),seq_99,99)),1)
where seq_99 is a Named Formula that refers to:
=IF(ROW(INDEX($1:$65535,1,1):INDEX($1:$65535,255,1))=1,1,(ROW(INDEX($1:$65535,1,1):INDEX($1:$65535,255,1))-1)*99)
The function results in an array, some of the values are numeric; the AGGREGATE function returns the largest value in the array, ignoring errors.
The formulas below are for earlier versions of Excel and must be entered as array formulas, by holding down ctrl + shift while hitting enter If you do this correctly, Excel will place braces {...} around the formula.
If you have 2007, you can use IFERROR
=MAX(IFERROR(--TRIM(MID(SUBSTITUTE(SUBSTITUTE(A2,",",REPT(" ",99)),"=",REPT(" ",99)),seq_99,99)),0))
For earlier versions, you can use:
=MAX(IF(ISERROR(--TRIM(MID(SUBSTITUTE(SUBSTITUTE(A3,",",REPT(" ",99)),"=",REPT(" ",99)),seq_99,99))),0,--TRIM(MID(SUBSTITUTE(SUBSTITUTE(A3,",",REPT(" ",99)),"=",REPT(" ",99)),seq_99,99))))

Your decimal separator may be different from the US decimal separator.
Public Function MyCode(ByVal txt As String) As String
Dim maxi As Double, db As Double
maxi = -9 ^ 9
arr = Split(txt, ",")
For Each a In arr
If InStr(a, "=") Then
a = Mid(a, InStr(a, "=") + 1)
ar = Replace(a, ".", Format(0, "."))
If IsNumeric(ar) Then
db = ar
If db > maxi Then maxi = db: ok = True
End If
End If
Next a
If ok = True Then
MyCode = CStr(maxi)
End If
End Function

Collect all of the mixed numbers as doubles in an array and return the maximum value.
Option Explicit
Option Base 0 '<~~this is the default but I've included it because it has to be 0
Function maxNums(str As String)
Dim n As Long, nums() As Variant
Static rgx As Object, cmat As Object
'with rgx as static, it only has to be created once; beneficial when filling a long column with this UDF
If rgx Is Nothing Then
Set rgx = CreateObject("VBScript.RegExp")
End If
maxNums = vbNullString
With rgx
.Global = True
.MultiLine = False
.Pattern = "\d*\.\d*"
If .Test(str) Then
Set cmat = .Execute(str)
'resize the nums array to accept the matches
ReDim nums(cmat.Count - 1)
'populate the nums array with the matches
For n = LBound(nums) To UBound(nums)
nums(n) = CDbl(cmat.Item(n))
Next n
'test array
'Debug.Print Join(nums, ", ")
'return the maximum value found
maxNums = Application.Max(nums)
End If
End With
End Function

Related

String Function in Excel

Here are the sample text:
Input:
1. AbST/1234342
2. Y_sRR/666
3. B.RE/1234
Output:
1. AST
2. YRR
3. BRE
Basically I need a formula to get the first three capital letters before the slash and disregard lowercase and symbols.
I don't know how this could be done without VBA, but with VBA it is straightforward enough:
Function ExtractUppers(s As String, k As Long) As String
'Extracts the first k upper case letters from
's, returning the result as a string
'if there are fewer than k, the function returns
'those that are found
Dim letters As Variant
Dim i As Long, j As Long
Dim c As String
ReDim letters(0 To k - 1) As String
For i = 1 To Len(s)
c = Mid(s, i, 1)
If "A" <= c And c <= "Z" Then
letters(j) = c
j = j + 1
If j = k Then Exit For
End If
Next i
ExtractUppers = Join(letters, "")
End Function
Then, for example, =ExtractUppers("B.RE/1234",3) would evaluate to "BRE".
Here is another formula option if you have access to CONCAT function. Check the link for more information.
Formula in B1:
=CONCAT(IF(CODE(MID(A1,ROW($A$1:INDEX(A:A,LEN(A1))),1))=TRANSPOSE(ROW($A$65:$A$90)),MID(A1,ROW($A$1:INDEX(A:A,LEN(A1))),1),""))
NOTE: It's an array formula that you need to enter through Ctrl+Shift+Enter
I have not taken into consideration the "/" as though it seems there are not going to be capitals after it in your sample data. If there are, you have to replace the above formula with:
=CONCAT(IF(CODE(MID(LEFT(A1,SEARCH("/",A1)),ROW($A$1:INDEX(A:A,SEARCH("/",A1))),1))=TRANSPOSE(ROW($A$65:$A$90)),MID(LEFT(A1,SEARCH("/",A1)),ROW($A$1:INDEX(A:A,SEARCH("/",A1))),1),""))
If you have Office 365 / Excel 2016+ with the TEXTJOIN function, you can use the array formula:
=IFERROR(LEFT(TEXTJOIN("",TRUE,IF((CODE(MID(A1,ROW(INDIRECT("1:"&FIND("/",A1)-1)),1))>=65)*(CODE(MID(A1,ROW(INDIRECT("1:"&FIND("/",A1)-1)),1))<=90),MID(A1,ROW(INDIRECT("1:"&FIND("/",A1)-1)),1),"")),3),"")
As pointed out by #JvdV, you can replace the TEXTJOIN function with the CONCAT function which is simpler when a delimiter is not required:
=IFERROR(LEFT(CONCAT(IF((CODE(MID(A1,ROW(INDIRECT("1:"&FIND("/",A1)-1)),1))>=65)*(CODE(MID(A1,ROW(INDIRECT("1:"&FIND("/",A1)-1)),1))<=90),MID(A1,ROW(INDIRECT("1:"&FIND("/",A1)-1)),1),"")),3),"")
To enter/confirm an array formula, hold down ctrl + shift while hitting enter. If you do this correctly, Excel will place braces {...} around the formula seen in the formula bar.
If you do not have the TEXTJOIN function, you can use a User Defined Function written in VBA.
Here is an example using Regular Expressions:
Option Explicit
Function upper3(S As String) As String
Dim RE As Object, MC As Object
Dim I As Long, sTemp As String
Set RE = CreateObject("vbscript.regexp")
With RE
.Global = True
.ignorecase = False
.Pattern = "[A-Z](?=.*/)"
Set MC = .Execute(S)
If MC.Count >= 3 Then
For I = 0 To 2
sTemp = sTemp & MC(I)
Next I
upper3 = sTemp
End If
End With
End Function
Either gives the following results:
However, the formula will return 1-3 uppercase letters preceding the /; the UDF only returns a result if there are at least 3 uppercase letters preceding the /. Either can be changed to the other depending on your requirements.

How to Find Average of Numbers within a Single Cell

I've been working on this problem for awhile now and I am able to break this out using multiple cells and eventually get the average. But I am unable to construct a single formula to work out the average.
The data will be dynamic, from 2 to more than 8.
Idea is that I can paste the data into a cell and the Average will compute in another cell.
As mentioned, I was able to break it out using many separate cells and also vba, but was thinking of making the spreadsheet clear with a single cell to do this work.
Using this as an example data set:
ABC 106.375/DF 106.99/G 106.5/JK 99.5/
Output: Average = 104.84125
Just trying to retrieve the 3rd number in the data has put my formula into a huge mess and unneeded complication. =MID(G3,LEN(LEFT(G3,FIND("/",G3)-1))+LEN(MID(G3,LEN(LEFT(G3,FIND("/",G3)-1))+2,FIND("/",G3,LEN(LEFT(G3,FIND("/",G3)-1)))-2))+3,FIND("/",G3,LEN(LEFT(G3,FIND("/",G3)-1))+LEN(MID(G3,LEN(LEFT(G3,FIND("/",G3)-1))+2,FIND("/",G3,LEN(LEFT(G3,FIND("/",G3)-1)))-2))+3)-(LEN(LEFT(G3,FIND("/",G3)-1))+LEN(MID(G3,LEN(LEFT(G3,FIND("/",G3)-1))+2,FIND("/",G3,LEN(LEFT(G3,FIND("/",G3)-1)))-2))+2)-1)
I feel so limited that i am unable to keep variables and I am not even at the point when can pull all the numbers together to compute the average.
Here's a regex-based user defined function.
Option Explicit
Function avgNumsOnly(str As String, _
Optional delim As String = ", ")
Dim n As Long, nums() As Variant
Static rgx As Object, cmat As Object
'with rgx as static, it only has to be created once; beneficial when filling a long column with this UDF
If rgx Is Nothing Then
Set rgx = CreateObject("VBScript.RegExp")
End If
avgNumsOnly = vbNullString
With rgx
.Global = True
.MultiLine = False
.Pattern = "\d*\.?\d+"
If .Test(str) Then
Set cmat = .Execute(str)
'resize the nums array to accept the matches
ReDim nums(cmat.Count - 1)
'populate the nums array with the matches
For n = LBound(nums) To UBound(nums)
nums(n) = CDbl(cmat.Item(n))
Next n
'average the nums array
avgNumsOnly = Application.Average(nums)
End If
End With
End Function

How to remove all numeric characters separated by white space from an Excel cell?

I need to remove the numeric characters that are separated by white space ONLY in a text string in an Excel cell. For example I have:
johndoe99#mail.com 1 concentr8 on work VARIABLE1 99
I need to get:
johndoe99#mail.com concentr8 on work VARIABLE1
Either formula or VBA script solution is good. Thank you.
I think nomad is right that regex is probably a simpler option. However, I also think that by using the Split() and isNumeric() functions I've come up with a good solution here.
Sub test()
Dim cell As Range
For Each cell In Range("A1:A10") 'adjust as necessary
cell.Value2 = RemoveNumbers(cell.Value2)
Next cell
End Sub
Function RemoveNumbers(ByVal inputString As String) As String
Dim tempSplit As Variant
tempSplit = Split(inputString, " ")
Dim result As String
Dim i As Long
For i = LBound(tempSplit) To UBound(tempSplit)
If Not IsNumeric(tempSplit(i)) Then result = result & " " & tempSplit(i)
Next i
RemoveNumbers = Trim$(result)
End Function
UDF
Function RemNum(cell)
With CreateObject("VBScript.RegExp")
.Global = True: .Pattern = "\s\d+"
RemNum = .Replace(cell, vbNullString)
End With
End Function
Note that in addition to testing for spaces before and after, this also tests for the beginning or end of the string as a delimiter.
You did not indicate the case where the number is the only contents of the string. This routine will remove it but, if you want something else, specify.
Try this:
Function remSepNums(S As String) As String
With CreateObject("vbscript.regexp")
.Global = True
.Pattern = "(?:\s+|^)(?:\d+)(?=\s+|$)"
.MultiLine = True
remSepNums = .Replace(S, "")
End With
End Function
Just for fun, if you have a recent version of Excel (Office 365/2016) you can use the following array formula:
=TEXTJOIN(" ",TRUE,IF(NOT(ISNUMBER(FILTERXML("<t><s>"&SUBSTITUTE(TRIM(A1)," ","</s><s>")&"</s></t>","//s"))),FILTERXML("<t><s>"&SUBSTITUTE(TRIM(A1)," ","</s><s>")&"</s></t>","//s"),""))
FILTERXML can be used to split the string into an array of words, separated by spaces
If any word is not a number, return that word, else return a null string
Then join the segments using the TEXTJOIN function.

extract multiple expressions

I have a cell that contains usernames assigned to projects like this
,FC757_random_name,AP372_another_one,FC782_again_different,FC082_samesamebutdifferent,
I need to only extract the alphanumeric values the expressions start with, so everything in between , and _.
I made it work for one expression with the following, but I need all of them.
= MID(A1;FIND(",";A1)+1;FIND("_";A1)-FIND(",";A1)-1)
I also tinkered with Text to Data, but couldn't make it work for multiple lines at once.
Ideally this would work only with formulas, but I guess (/fear) I'll need VBA or Macros, which I have never worked with before.
All help will be appreciated!
Here is a regex based User Defined Function.
Option Explicit
Function extractMultipleExpressions(str As String, _
Optional delim As String = ", ")
Dim n As Long, nums() As Variant
Static rgx As Object, cmat As Object
'with rgx as static, it only has to be created once; beneficial when filling a long column with this UDF
If rgx Is Nothing Then
Set rgx = CreateObject("VBScript.RegExp")
End If
extractMultipleExpressions = vbNullString
With rgx
.Global = True
.MultiLine = False
.Pattern = "[A-Z]{2}[0-9]{3}"
If .Test(str) Then
Set cmat = .Execute(str)
'resize the nums array to accept the matches
ReDim nums(cmat.Count - 1)
'populate the nums array with the matches
For n = LBound(nums) To UBound(nums)
nums(n) = cmat.Item(n)
Next n
'convert the nums array to a delimited string
extractMultipleExpressions = Join(nums, delim)
End If
End With
End Function
I believe you are looking for something like this Press Alt + F11 and then choose Insert > Module and then paste the following code:
Public Function GetUsers(UserNameProject As String)
Dim userArray() As String
Dim users As String
Dim intPos As Integer
'this will split the users into an array based on the commas
userArray = Split(UserNameProject, ",")
'loop through the array and process any non blank element and extract user
'based on the position of the first underscore
For i = LBound(userArray) To UBound(userArray)
If Len(Trim(userArray(i))) > 0 Then
intPos = InStr(1, userArray(i), "_")
users = users & "," & Left(userArray(i), intPos - 1)
End If
Next
GetUsers = users
End Function
If your string is in A1 then use by putting =GetUsers(A1) in the approiate cell. I think this should get you started!
To clean the data of extra commas, use this formula in cell B1:
=TRIM(SUBSTITUTE(SUBSTITUTE(TRIM(SUBSTITUTE(SUBSTITUTE(A1;" ";"||");";";" "));" ";";");"||";" "))
Then use this formula in cell C1 and copy over and down to extract just the part you want from each section:
=IFERROR(INDEX(TRIM(LEFT(SUBSTITUTE(TRIM(MID(SUBSTITUTE($B1;";";REPT(" ";LEN($B1)));LEN($B1)*(ROW($A$1:INDEX($A:$A;LEN($B1)-LEN(SUBSTITUTE($B1;";";""))+1))-1)+1;LEN($B1)));"_";REPT(" ";LEN($B1)));LEN($B1)));COLUMN(A1));"")

Excel function to VBA

I have a column (E) of items that are filled with codes that resemble the following format: 5301-500-300-000
with an adjacent column (F) of 'amounts paid' that look like the following: 53.20
My goal is to multiply the appropriate amounts in column F with the right tax rebates by using a nested if formula in vba. I've managed to do this using excel functions as follows:
a left(E2,4) formula
& a mid(E2,10,2) formula followed by a
=IF(OR(F282=1151,F282=1153),IF(OR(G282=131,G282=200,G282=210,G282=300,G282=310,G282=320,G282=800,G282=821,G282=831,G282=841,,G282=700,G282=721),H282*0.5,IF(OR(G282=341,G282=351,G282=400,G282=410,G282=421,G282=431,G282=441,G282=500,G282=511,G282=521,G282=531,G282=600,G282=611,G282=900,G282=700,G282=721),H282*0.3031,0))) formula
My question is how could I convert this series of excel formulas into a vba format so that I wouldn't have to constantly use the LEFT & MID excel functions.
So far, I've tried creating variables for left' &mid `
Private Sub CommandButton2_Click()
Dim taxcode As Range, location As Range
Set taxcode = Left(Range("E2:E10000"), 4)
Set location = Mid(Range("E2:E10000"), 10, 2)
End Sub
But have already seen problems with my code. Any help would be most appreciated.
I would use a regular expression for this sort of thing; that way you can avoid having those awfull nested LEFT() and MID() stuff.
So, let's get to it.
First, in the VBA editor, clic on the Tools menu and select References; enable Microsoft VBScript Regular Expressions 5.5.
Then, let's use a RegEx to split each entry from your string:
Function splitCode(code As String) As String()
Dim ans(1 To 4) As String
Dim re As RegExp
Set re = New RegExp
With re
.IgnoreCase = True
.MultiLine = False
.Pattern = "([0-9]*)-([0-9]*)-([0-9]*)-([0-9]*)"
' Here's the magic:
' [0-9]* will match any sequence of digits
' The parenthesis will help you retreive each piece of the pattern
End With
If re.Test(code) Then
ans(1) = re.Replace(code, "$1") ' You can use the Replace method to get
ans(2) = re.Replace(code, "$2") ' each piece of the pattern.
ans(3) = re.Replace(code, "$3") ' Simply use $n (where n is an integer)
ans(4) = re.Replace(code, "$4") ' to get the n-th piece of the pattern enclosed in parenthesis
End If
Set re = Nothing
splitCode = ans
End Function
Now that you have this array with each piece of your code, you can use it in other sub or function to get what you need:
sub doMyStuff()
dim taxCodeRange as Range, taxCode as String()
dim i as integer
taxCodeRange = Range("E2:E1000")
for i = 1 to taxCodeRange.Rows.count
taxCode = splitCode(taxCodeRange.Cells(i,1))
' Now you can make whatever comparissons you need with each entry
' of the taxCode array.
' WARNING: Each entry in the array is a String, so you may want
' to convert it to integer before doing any comparissons
if CInt(taxCode(1)) = 5301 then
' Do some stuff
elseIf cInt(taxCode(1)) = 5302 then
' Do some other stuff
' ...
' ...
' end if
next i
end sub
Hope this helps you.
Take a look to this post for more information about Regular Expressions in Excel

Resources