I have Excel 2007. I am trying to find the largest number in a cell that contains something like the following:
[[ E:\DATA\SQL\SY0\ , 19198 ],[ E:\ , 18872 ],[ E:\DATA\SQL\ST0\ , 26211 ],[ E:\DATA\SQL\ST1\ , 26211 ],[ E:\DATA\SQL\SD0\ , 9861 ],[ E:\DATA\SQL\SD1\ , 11220 ],[ E:\DATA\SQL\SL0\ , 3377 ],[ E:\DATA\SQL\SL1\ , 1707 ],[ E:\DATA\SQL_Support\SS0\ , 14375 ],[ E:\DATA\SQL_Support\SS1\ , 30711 ]]
I am not a coder but I can get by with some basic instructions. If there is a formula that can do this, great! If the best way to do this is some sort of backend code, just let me know. Thank you for your time.
I do have the following formula that almost gets me there:
=SUMPRODUCT(MID(0&A2,LARGE(INDEX(ISNUMBER(--MID(A2,ROW(INDIRECT("1:"&LEN($A$2))),1))*ROW(INDIRECT("1:"&LEN($A$2))),0),ROW(INDIRECT("1:"&LEN($A$2))))+1,1)*10^ROW(INDIRECT("1:"&LEN($A$2)))/10)
With a cell that contains a string like above, it will work. However, with a string that contains something like:
[[ E:\DATA\SQL\SY0\ , 19198.934678 ],[ E:\ , 18872.2567 ]]
I would end up with the value of 19198934678 as the largest value.
You can use this UDF:
Function MaxInString(rng As String) As Double
Dim splt() As String
Dim i&
splt = Split(rng)
For i = LBound(splt) To UBound(splt)
If IsNumeric(splt(i)) Then
If splt(i) > MaxInString Then
MaxInString = splt(i)
End If
End If
Next i
End Function
Put this in a module attached to the workbook. NOT in the worksheet or ThisWorkbook code.
Then you can call it like any other formula:
=MaxInString(A1)
If there is always a space before and after, you can use this formula. The formula is an array formula and must be confirmed by holding down ctrl + shift while hitting enter
With your string in A1:
=MAX(IFERROR(--TRIM(MID(SUBSTITUTE(A1," ",REPT(" ",99)),IF(seq=1,1,(seq-1)*99),99)),0))
seq is a defined name that refers to:
=ROW(INDEX(Sheet1!$1:$65536,1,1):INDEX(Sheet1!$1:$65536,255,1))
If a VBA UDF is preferable, I suggest the following. The Regex will match anything that might be a number. The number is expected to be in the format of iiii.dddd The integer part and the decimal point are both optional.
Option Explicit
Function LargestNumberFromString(S As String) As Double
Dim RE As Object, MC As Object, M As Object
Dim D As Double
Set RE = CreateObject("vbscript.regexp")
With RE
.Global = True
.Pattern = "\b[0-9]*\.?[0-9]+\b"
If RE.test(S) = True Then
For Each M In MC
D = IIf(D > CDbl(M), D, CDbl(M))
Next M
End If
End With
End Function
This is going to be a rather complex answer for someone with no programming background, so be prepared to spend a lot of time covering and researching this topic if you really wish to achieve a function that finds the largest number in a string in excel.
The solution, requires the use of VBA and Regular expressions
VBA is used in excel when there is a need for more complex functionality that just can't be achieved with the use of built in spreadsheet functions.
Regular expressions are a language used to tell programs how to extract useful information from texts, in this case we can extract all the numbers in your text. with the following regular expression.
(\d+.?\d*)/g
Which roughly means: Match one or more digits with an optional period and subsequent optional digits.
The program that will interpret this will do the following: Look for digits, if you see one, then that's a match, grab all contiguous digits and add them to the match. Once you find a character that is not a digit, start looking for new matches. if at any point you find a dot, add it to the match, but just once, and keep on looking for digits. Rinse and repeat until the end of the text.
You can test it here. In this case, the regex matches 19 numbers.
http://www.regextester.com/
Once you have a collection with the 19 matches (See link to regular expressions), all you would need to do is to loop over each of the matches to find out which of the numbers is the highest:
for each number in matches
if number > highestNumber then
highestNumber = number
end if
next
And highestNumber will be the the result! In order to have this code run in a simple custom function, you can follow this microsoft tutorial ( https://support.office.com/en-us/article/Create-Custom-Functions-in-Excel-2007-2f06c10b-3622-40d6-a1b2-b6748ae8231f?ui=en-US&rs=en-US&ad=US&fromAR=1 )
Where c is your string to find max from
Dim qwe() As String
qwe = Split(c, ", ")
maxed = 0
For x = LBound(qwe) To UBound(qwe)
qwe(x) = Left(qwe(x), InStr(1, qwe(x), " ", vbBinaryCompare))
On Error Resume Next
If CLng(qwe(x)) > maxed Then maxed = CLng(qwe(x))
Next x
MsgBox maxed
The error line is there to ignore when qwe(x) cannot be converted to a LONG number.
I must say this is very specific to your string format, for a more comprehensive doodad you'd want to have the split delimiter as a variable and possibly use the "IsNumeric" function to scan the entire string.
Related
I have the following strings from which I need to extract 6 digit numbers. Since these strings are generated by another software, they occur interchangeably and I cannot control it. Is there any one method that would extract both 6-digit numbers from each of these strings?
Branch '100235 to 100236 Ckt 1' specified in table 'East Contingency' for record with primary key = 21733 was not found in branch or transformer data.
Loadflow branch ID '256574_701027_1' defined in supplemental branch table was not found in branch or transformer input.
Transmission element from bus number 135415 to bus number 157062 circuit ID = 1 defined for corridor 'IESO-NYISO' was not found in input data
I don't know VBA, but I can learn it if it means I can get the 6 digit numbers using a single method.
thanks
I have been using LEFT(), RIGHT() & MID() previously, but it means manually applying the appropriate formula for individual string.
If you have Microsoft 365, you can use this formula:
=LET(arr,TEXTSPLIT(SUBSTITUTE(SUBSTITUTE(A1,"'"," "),"_"," ")," "),
FILTER(arr,ISNUMBER(-arr)*(LEN(arr)=6)))
Thanks to #TomSharpe for this shorter version, using an array constant within TEXTSPLIT to add on possible delimiters.
=LET(arr,TEXTSPLIT(A1,{"_"," ",","," ","'"}),FILTER(arr,(LEN(arr)=6)*ISNUMBER(-arr)))
Data
Output
An alternative is:
=LET(ζ,MID(A1,SEQUENCE(,LEN(A1)-5),6),ξ,MID(ζ,SEQUENCE(6),1),FILTER(ζ,MMULT(SEQUENCE(,6,,0),1-ISERR(0+ξ))=6))
A couple more suggestions (if you need them):
(1) Replacing all non-digit characters with a space then splitting the resulting string:
=LET(numbers,TEXTSPLIT(TRIM(REDUCE("",MID(A1,SEQUENCE(1,LEN(A1)),1),LAMBDA(a,c,IF(is.digit(c),a&c,a&" "))))," "),FILTER(numbers,LEN(numbers)=6))
Here I've defined a function is.digit as
=LAMBDA(c, IF(c = "", FALSE, AND(CODE(c) > 47, CODE(c) < 58)))
(tl;dr I quite like doing it this way because it hides the implementation details of is.digit and creates a rudimentary form of encapsulation)
(2) A UDF - based on the example here and called as
=RegexTest(A1)
Option Explicit
Function RegexTest(s As String) As Double()
Dim regexOne As Object
Dim theNumbers As Object
Dim Number As Object
Dim result() As Double
Dim i As Integer
Set regexOne = New RegExp
' Not sure how you would extract numbers of length 6 only, so extract all numbers...
regexOne.Pattern = "\d+"
regexOne.Global = True
regexOne.IgnoreCase = True
Set theNumbers = regexOne.Execute(s)
i = 1
For Each Number In theNumbers
'...Check the length of each number here
If Len(Number) = 6 Then
ReDim Preserve result(1 To i)
result(i) = CDbl(Number)
i = i + 1
End If
Next
RegexTest = result
End Function
Note - if you wanted to preserve leading zeroes you would need to omit the Cdbl() and return the numbers as strings. Returns an error if no 6-digit numbers are found.
I have a column of Hexadecimal strings with many TRAILING zeros.
The problem i have is that the trailing Zeros from the string, needs to be removed
I have searched for a VBA formula such as Trim but my solution has not worked.
Is there a VBA formula I can use to remove all these Trailing zeros from each of the strings.
An example of the HEX string is 4153523132633403277E7F0000000000000000000000000000. I would like to have it in a format of 4153523132633403277E7F
The big issue is that the Hexadecimal strings can be of various lengths.
Formula:
You could try:
Formula in B1:
=LET(a,TEXTSPLIT(A1,,"0"),TEXTJOIN("0",0,TAKE(a,XMATCH("?*",a,2,-1))))
This would TEXTSPLIT() the input and the fact that we can then use XMATCH() to return the position of the last non-empty string with a wildcard match ?*. However, given the fact we can use arrays in our TEXTSPLIT() function, a little less verbose could be:
=TEXTBEFORE(A1,TAKE(TEXTSPLIT(A1,TEXTSPLIT(A1,"0",,1)),,-1),-1)
Or another option, though more verbose, is to use REDUCE() for what it's intended to do, which is to loop a given array:
=REDUCE(A1,SEQUENCE(LEN(A1)),LAMBDA(a,b,IF(RIGHT(a)="0",LEFT(a,LEN(a)-1),a)))
VBA:
If VBA is a must, one way of dealing with this is through the RTrim() function. Since your HEX-string should not contain spaces to begin with I think the following is a safe bet:
Sub Test()
Dim s As String: s = "4153523132633403277E7F0000000000000000000000000000"
Dim s_new As String
s_new = Replace(RTrim(Replace(s, "0", " ")), " ", "0")
Debug.Print s_new
End Sub
If you happen to have spaces anywhere else in your string, another option would be to look for trailing zero's using a regular expression:
Sub Test()
Dim s As String: s = "4153523132633403277E7F0000000000000000000000000000"
Dim s_new As String
With CreateObject("vbscript.regexp")
.Pattern = "0+$"
s_new = .Replace(s, "")
End With
Debug.Print s_new
End Sub
Both the above options should print: 4153523132633403277E7F
As far as I know, there is no function to do that for you. The way I would do it is presented in the pseudo-code below:
while last character is "0"
remove last character
end while
It is quit slow, but VBA itself is not race car either, so you will probably not notice especially if you do not need to that for many times at once.
A more beautiful solution would involve VBA being able to search for the beginning or the end of a string.
An improvement of the solution above is to parse the string backwards and count the "0" characters, and then remove them all at the same time.
I am trying to read an excel sheet and then and find cells that are not empty and have date information in them by finding two '/' in a string
but matlab keeps to erroring on handling cell type
"Undefined operator '~=' for input arguments of type 'cell'."
"Undefined function 'string' for input arguments of type 'cell'."
"Undefined function 'char' for input arguments of type 'cell'."
MyFolderInfo = dir('C:\');
filename = 'Export.xls';
[num,txt,raw] = xlsread(filename,'A1:G200');
for i = 1:length(txt)
if ~isnan(raw(i,1))
if sum(ismember(char(raw(i,1)),'/')) == 2
A(i,1) = raw(i,1);
end
end
end
please help fixing it
There are multiple issues with your code. Since raw is a cell array, you can't run isnan on it, isnan is for numerical arrays. Since all you're interested in is cells with text in them, you don't need to use raw at all, any blank cells will not be present in txt.
My approach is to create a logical array, has_2_slashes, and then use it to extract the elements from raw that have two slashes in them.
Here is my code. I generalized it to read multiple columns since your original code only seemed to be written to handle one column.
filename = 'Export.xls';
[~, ~, raw] = xlsread(filename, 'A1:G200');
[num_rows, num_cols] = size(raw);
has_2_slashes = false(num_rows, num_cols);
for row = 1:num_rows
for col = 1:num_cols
has_2_slashes(row, col) = sum(ismember(raw{row, col}, '/')) == 2;
end
end
A = raw(has_2_slashes);
cellfun(#numel,strfind(txt,'/'))
should give you a numerical array where the (i,j)th element contains the number of slashes. For example,
>> cellfun(#numel,strfind({'a','b';'/','/abc/'},'/'))
ans =
0 0
1 2
The key here is to use strfind.
Now you may want to expand a bit in your question on what you intend to do next with txt -- in other words, specify desired output more, which is always a good thing to do. If you intend to read the dates, it may be better to just read it upfront, for example by using regexp or datetime as opposed to getting an array which can then map to where the dates are. As is, using ans>=2 next gives you the logical array that can let you extract the matched entries.
I am currently building a numberplate checker on an excel spread sheet that will determine if the letters and numbers of the numberplate are in the correct places and are valid.
The 3 criteria I have are if the numberplates are in on of these formulas:
(I have represented a number as 1 and a letter as A)
AAA111A
A111AAA
AA11AAA
The ultimate objective is for the program to ask the question "Look at these number plates, do they follow a format as shown above."
So far I have only been able to check to see if I have numbers in certain places, however I cannot specify the characters A - Z when trying to do a search function from the left, right and centre.
=ISNUMBER(--MID(A3,1,3))
If I wanted to search within a cell for example, the first character, is it a letter a-z, return true or false? How would I go about doing this?
An example in this instance might be:
DJO148R
The formula
=ISNUMBER(--MID(A5,4,3))
This would turn back as true because the 4th character is a number and so are the next 2.
With the same numberplate, how do I change it to search for letters rather than numbers within the numberplate?
Here is a simpler RegEx implementation. Make sure you include references to Microsoft VBScript Regular Expressions 5.5. This will go in a new inserted module
Function PlateCheck(cell As Range) As Boolean
Dim rex As New RegExp
rex.Pattern = "[A-Z][0-9|A-Z][0-9|A-Z][0-9|A-Z][0-9|A-Z][0-9|A-Z][A-Z]"
If rex.Test(cell.Value) Then
PlateCheck = True
Else
PlateCheck = False
End If
End Function
As per the guys comments, here's how you do it with regex:
Make sure to include MS VB regular expressions 5.5 as a reference.
To do that, in your VBA IDE, go Tools, Reference and then look the regex reference.
Then Add this in a new module:
Function VerifyLicensePlate(ip As Range) As String
Dim regex As New RegExp
Dim inputstr As String: inputstr = ip.Value
With regex
.Global = True
.IgnoreCase = True
End With
Dim strpattern(2) As String
strpattern(0) = "[A-Z][A-Z][A-Z][0-9][0-9][0-9][A-Z]"
strpattern(1) = "[A-Z][A-Z][0-9][0-9][A-Z][A-Z][A-Z]"
strpattern(2) = "[A-Z][0-9][0-9][0-9][A-Z][A-Z][A-Z]"
For i = 0 To 2
regex.pattern = strpattern(i)
If regex.Test(inputstr) Then
VerifyLicensePlate = "Match"
Exit Function
Else
VerifyLicensePlate = "No match"
End If
Next
End Function
Output:
Occam's Razor would suggest,
=NOT(ISNUMBER(--MID(A5,4,3)))
... or,
=ISERROR(--MID(A5,4,3))
Here's a version that uses late-binding, so no need to set a reference. IT is case insensitive, as that seemed to be implied in your question, but that is easily changed.
Option Explicit
Function MatchPattern(S As String) As Boolean
Dim RE As Object
Set RE = CreateObject("vbscript.regexp")
With RE
.Global = True
.Pattern = "\b(?:[A-Z]{3}\d{3}[A-Z]|[A-Z]{2}\d{2}[A-Z]{3}|[A-Z]\d{3}[A-Z]{3})\b"
.ignorecase = True
MatchPattern = .test(S)
End With
End Function
But, as pointed out by G Serg, you don't really need regex for this:
Option Explicit
Option Compare Text 'Case Insensitive
Function MatchPattern(S As String) As Boolean
Const S1 As String = "[A-Z][A-Z][A-Z]###[A-Z]"
Const S2 As String = "[A-Z]###[A-Z][A-Z][A-Z]"
Const S3 As String = "[A-Z][A-Z]##[A-Z][A-Z][A-Z]"
MatchPattern = False
If Len(S) = 7 Then
If S Like S1 Or _
S Like S2 Or _
S Like S3 Then _
MatchPattern = True
End If
End Function
Here is a rather complicated formula that seems to match your specifications:
=AND(LEN(A1)=7,
OR(MMULT(--(CODE(MID(A1,{1,2,3,4,5,6,7},1))>64),--(TRANSPOSE(CODE(MID(A1,{1,2,3,4,5,6,7},1))<91)))={4,5}),
CODE(LEFT(A1,1))>64,CODE(LEFT(A1,1))<91,
CODE(RIGHT(A1,1))>64,CODE(RIGHT(A1,1))<91,
ISNUMBER(-MID(A1,MIN(FIND({1,2,3,4,5,6,7,8,9,0},A1&"0123456789")),
7-MMULT(--(CODE(MID(A1,{1,2,3,4,5,6,7},1))>64),--(TRANSPOSE(CODE(MID(A1,{1,2,3,4,5,6,7},1))<91))))))
Ensure we have only seven characters
The OR(MMULT... function counts the number of letters and returns TRUE if four or five.
Check to make sure first and last character is a letter
There should remain a consecutive string of either two or three digits (seven less the number of letters)
If you want to make the formula case insensitive, replace the instances of A1 with UPPER(A1)
I think the UDF solution is better.
Most people ask how to get the characters from a string, which can be done by Mid(). I am trying to assign a string character by character in VBA code. The characters to be assigned depend on some calculated results.
I do not want to use string concatenation to form the string.
I have searched the web, but the posted solution, strName.Chars(i) (e.g., at MS development network), is not recognized in my 2007 Access VBA.
Thanks
You can use Mid to set values, too.
Sub showMidExample()
Dim s As String
s = "aaaaa"
Dim i As Integer
For i = 1 To Len(s)
Mid(s, i) = "n"
Debug.Print s
Next i
End Sub
This prints out
naaaa
nnaaa
nnnaa
nnnna
nnnnn
Which is what you are looking for.
Since no working answer is posted. I assume that cannot be done in VBA and I have to use concatenation to form the string although that is cumbersome in my case.