I've the following Excel data:
A B C
+ ------------ ------------- -----------------
1 | WORD WORD MIX MATCH TEXT RESULT
2 | somewordsome emsomordsowe ...
3 | anotherword somethingelse ...
4 | ... ... ...
I'd like to:
Firstly, get an array, say ArrayOfGroups, by splitting the string in the A2 cell in unique groups of 2 to 12 adjacent chars (note: 2 is the minimum number of chars to form a group; 12 is the total number of the word's chars) i.e. the groups of 2 chars would be so, om, me, ew, wo, or, rd, ds (note: the last so, om and me groups are excluded because they are repeated); the groups of 3 chars would be som, ome, mew, ewo, wor, ord, rds, dso (last som and ome excluded); the groups of 4 chars would be some, omew, mewo, ewor, word, ords, rdso, dsom; ... and so on until the full string somewordsome.
Then, iterate the above-mentioned ArrayOfGroups to check if each of its element is a substring of the B2 cell and return a new array, say ArrayOfMatches, containing all the elements (the characters "group names") that are substrings of B2 and the number of occurrences found in B2.
Finally, output in the C2 cell a sentence built using the ArrayOfMatches data that says something like this:
2 matches for so, 1 match for som and rd
Probably there are other and better approaches to compute the above sentence that is the final result wanted. Maybe I need to use a User Defined Function... but I never made it.
Is there someone that could give help?
May try something like this
Code edited to avoid counting for same substring found multiple times.
Sub test2()
Dim Xstr As String, Ystr As String
Xstr = "somewordsome"
Ystr = "emsomordsowe"
MsgBox Xmatch2(Xstr, Ystr)
End Sub
Function Xmatch2(Xstr As String, Ystr As String) As String
Dim XSubStr As String, YSubStr As String
Dim xLn As Integer, yLn As Integer
Dim XArr As Variant, LnSubStr As Integer
Dim Rslt As String, Cnt As Integer
Dim Xrr() As Variant, Xcnt As Integer, Chk As Boolean
Rslt = "'"
xLn = Len(Xstr)
yLn = Len(Ystr)
For LnSubStr = 2 To xLn 'length of substring
Xcnt = 0
ReDim XArr(1 To 1)
For Y = 1 To xLn
XSubStr = ""
Xcnt = Xcnt + 1
ReDim Preserve XArr(1 To Xcnt)
If Y + LnSubStr - 1 <= xLn Then XSubStr = Mid(Xstr, Y, LnSubStr)
XArr(Xcnt) = XSubStr
Chk = False
For i = 1 To Xcnt - 1
If XArr(i) = XSubStr Then
Chk = True
Exit For
End If
Next
If XSubStr <> "" And Chk = False Then
Cnt = 0
ReDim Preserve XArr(1 To Xcnt)
For Z = 1 To yLn
YSubStr = ""
If Z + LnSubStr - 1 <= yLn Then YSubStr = Mid(Ystr, Z, LnSubStr)
If YSubStr = XSubStr Then Cnt = Cnt + 1
Next
If Cnt > 0 Then Rslt = Rslt & Cnt & " Matches for " & XSubStr & ","
End If
Next
Next
Debug.Print Rslt
Xmatch2 = Rslt
End Function
Related
I'm trying to turn general data written as fractions like 3/4" or 13 7/32" into 3 place decimal numbers such as 0.750 or 13.219.
I have a working table replacement that handles 0 to 1" fractions. It can't handle the mixed numbers like 13 7/32". It leaves me with 13 0.219 which is why I need to replace " 0." with "." to join the 13 and 219 together with a decimal.
We do this data conversion in multiple steps and hand type because Excel tries converting some fractions like 3/4" into a date.
Original data
Resulting data
Sub FractionConvertMTO()
'this section works
For i = 6 To 70
Worksheets("BOM").Range("F6:H48").Select
Selection.Replace what:=Cells(i, 21).Value, Replacement:=Cells(i, 22).Value, _
LookAt:=xlPart, SearchOrder:=xlByRows, MatchCase:=False
Next
'this section doesn't work
For i = 6 To 70
Worksheets("BOM").Range("F6:H48").Select
str1 = " "
str1 = Trim(Replace(str1, " ", "+"))
Next
'this section changes the format.
For i = 66 To 130
Range("F6:H48").NumberFormat = "0.000"
Next
'this section is supposed to add an = sign in front of the cell contents but doesn't work.
Dim Cell As Range
For Each Cell In Range("F6:H48")
Cell.Value = "=" & Cell.Value
Next Cell
'this section works to highlight the first cell
Worksheets("BOM").Cells(1, 1).Select
End Sub
I dug up the following method from my library of useful functions. It converts numbers represented as a fractional string to the numeric equivalent. Simply loop through the cells needing conversion and call this method:
Public Function FractionToNumber(ByVal Value As String, Optional ByVal Digits As Long = 0) As Double
Dim P As Integer
Dim N As Double
Dim Num As Double
Dim Den As Double
Value = Trim$(Value)
P = InStr(Value, "/")
If P = 0 Then
N = Val(Value)
Else
Den = Val(Mid$(Value, P + 1))
Value = Trim$(Left$(Value, P - 1))
P = InStr(Value, " ")
If P = 0 Then
Num = Val(Value)
Else
Num = Val(Mid$(Value, P + 1))
N = Val(Left$(Value, P - 1))
End If
End If
If Den <> 0 Then N = N + Num / Den
FractionToNumber = Round(N, Digits)
End Function
You may also code something like the following:
Sub FractionConvertMTO()
Dim rng As Range
Dim Arr As Variant
Arr = Worksheets("MTO").Range("F6:H48")
For Row = 1 To UBound(Arr, 1)
For col = 1 To UBound(Arr, 2)
str1 = Arr(Row, col)
pos1 = InStr(str1, " ")
pos2 = InStr(str1, "/")
If pos2 = 0 Then
N = val(str1)
Num = 0: Den = 1
Else
If pos1 And pos1 < pos2 Then
N = val(Left$(str1, pos1 - 1))
Num = val(Mid$(str1, pos1 + 1))
Else
N = 0
Num = val(Left$(str1, pos2 - 1))
End If
Den = val(Mid$(str1, pos2 + 1))
End If
Arr(Row, col) = N + Num / Den
Next col
Next Row
Worksheets("MTO").Range("F6", "H48") = Arr
End Sub
If you dispose of the newer dynamic array features (vers. 2019+,MS365) you might write the results in one go to the entire original range (target range) as follows (overwriting the existing range; otherwise define a given offset to identify another target range: rng.Offset(,n)=..).
Tip: make a backup copy before testing (as it overwrites rng)!
Note that this example assumes the " character (asc value of 34).
A) First try via tabular VALUE() formula evaluation
Caveat: converting blanks by VALUE() would be written as #VALUE! results, which would need a further loop. To avoid this you can prefix a zero to the formulae myFormula = "=VALUE(SUBSTITUTE(" & """0""&" & rng.Address & ","""""""",""""))" so that results would be displayed as zero.
Sub ChangeToFractionValues()
'1) define original range to be replaced
Dim rng As Range
Set rng = ThisWorkbook.Worksheets("BOM").Range("F6:H48")
'2) define tabular formula
Dim myFormula As String
'myFormula = "=VALUE(SUBSTITUTE(" & rng.Address & ","""""""",""""))"
'Alternative to avoid #VALUE! displays for blanks:
myFormula = "=VALUE(SUBSTITUTE(" & """0""&" & rng.Address & ","""""""",""""))"
'Debug.Print myFormula
'3) overwrite original range (otherwise code an offset rng.Offset(,n).Value = ...
rng.Value2 = rng.Parent.Evaluate(myFormula)
End Sub
Conclusion due to comment:
Though fast, this approach has a big disadvantage: Excel interpretes date-like numbers as such, transforms them internally to dates by returning the numeric part here, so a cell input of 3/4" would return the corresponding date value of the current year for March 4th.
B) Reworked code based on direct cell evaluations in a loop //Edit
Similar to the above processing this approach is also based on evaluation, but collects all formulae as strings in a variant datafield array v, which allows to manipulate and evaluate each cell input individually:
Sub ChangeToFractionValues()
'1) define original range to be replaced
Dim rng As Range
Set rng = ThisWorkbook.Worksheets("BOM").Range("F6:H48")
'2) assign formula strings to variant 1-based 2-dim data field array
Dim v As Variant
v = rng.Formula2
'3) evaluate results in a loop
Dim i As Long, j As Long
For i = 1 To UBound(v)
For j = 1 To UBound(v, 2)
v(i, j) = Evaluate("0" & Replace(v(i, j), Chr(34), ""))
Next j
Next i
'4) overwrite original range (otherwise code an offset rng.Offset(,n).Value = ...
rng.Value = v
End Sub
str1 = trim(Replace(str1, "0.", "."))
For Example,
I'd like a String such as, "This is a Bunch of Words in a sequence of 13 possible 1 words from a Dictionary or BookZZ or Libgen.io 1876" to give me a result of 19 (because "13", "1876" and "1" are numbers and should not be counted).
I created Two Functions which I'm trying to use within this Function I'm asking about:
The first one is the following:
' NthWord prints out the Nth Word of a String of Text in an Excel Cell such
' as A1 or B19.
Function NthWord(ActiveCell As String, N As Integer)
Dim X As String
X = ActiveCell
X = Trim(Mid(Replace(ActiveCell, " ", Application.WorksheetFunction.Rept("
", Len(ActiveCell))), (N - 1) * Len(ActiveCell) + 1, Len(ActiveCell)))
NthWord = X
' In the Excel SpreadSheet:
' Trim (Mid(Substitute(A1, " ", Rept(" ", Len(A1))), (N - 1) * Len(A1)
' + 1, Len(A1)))
End Function
The second one is the following:
'NumberOfWords returns the number of words in a String
Function NumberOfWords(ActiveCell As String)
Dim X As String
X = ActiveCell
Dim i As Integer
i = 0
If Len(Trim(X)) = 0 Then
i = 0
Else:
i = Len(Trim(X)) - Len(Replace(X, " ", "")) + 1
End If
NumberOfWords = i
' In the Excel SpreadSheet
' IF(LEN(TRIM(A1))=0,0,LEN(TRIM(A1))-LEN(SUBSTITUTE(A1," ",""))+1)
End Function
My Attempt at printing the NumberOfNonNumberWords
Function NumberOfNonNumberWords(ActiveCell As String)
Dim X As String
X = ActiveCell
Dim count As Integer
count = 0
Dim i As Integer
If NumberOfWords(X) > 0 Then
For i = 1 To NumberOfWords(X)
If Not (IsNumeric(NthWord(X, i).Value)) Then
count = count + 1
End If
Next i
End If
NumberOfNonNumberWords = count
End Function
However, when I apply this function in the Excel Worksheet, I get an output of
#VALUE!
and I'm not sure why. How do I fix this?
Split the whole string then count non-numeric elements.
function abcWords(str as string) as long
dim i as long, arr as variant
arr = split(str, chr(32))
for i=lbound(arr) to ubound(arr)
abcWords = abcWords - int(not isnumeric(arr(i)))
next i
end function
You could just use SPLIT() to split the text on a space delimiter, then count the non-numeric words:
Function num_words(ByVal text As String)
Dim txt_split
txt_split = Split(text, " ")
Dim total_words As Long
total_words = 0
Dim i As Long
For i = LBound(txt_split) To UBound(txt_split)
If Not IsNumeric(txt_split(i)) Then
total_words = total_words + 1
End If
Next i
num_words = total_words
End Function
I'm facing a problem as a non dev. I have a column in Excel that contains info as such:
46843 xxxx xxx x
xxxx 65483 xxxx
xxxx xxx 65432 xxxxx 4 xx
"x" being normal caracters.
What I want is to be able to extract only the numbers of five digits only.
I started something like this but struggle to put a loop so that it scans all the string:
Function test()
val_in = "rue 4 qsdqsd CURIE 38320 EYBENS"
Filte = Left(val_in, 5)
If IsNumeric(Filte) Then
test = Left(val_in, 5)
Else
sp1 = InStr(1, val_in, " ")
sp2 = InStr(sp1 + 1, val_in, " ")
spt = sp2 + sp1
If spt > 5 Then
extr = Mid(val_in, spt, 5)
End If
End If
End Function
How could I turn the part after "Else" into a loop so that it would scan every space of the string and extract only the numbers that contains 5 digits?
Using regex
Option Explicit
Public Function GetNumbers(ByVal rng As Range) As Variant
Dim arr() As String, i As Long, matches As Object, re As Object
Set re = CreateObject("VBScript.RegExp")
With re
.Global = True
.MultiLine = True
.IgnoreCase = False
.Pattern = "\b\d{5}\b"
If .test(rng.Value) Then
Set matches = .Execute(rng.Value)
ReDim arr(0 To matches.Count - 1)
For i = LBound(arr) To UBound(arr)
arr(i) = matches(i)
Next i
Else
arr(i) = rng.Value
End If
End With
GetNumbers = Join(arr, ",")
End Function
Data:
If there is more than one match a comma separated list is returned.
Sub TestMe()
Dim valIn As String
valIn = "rue 4 qsdqsd CURIE 38320 EYBENS 43443"
Dim i As Long
Dim splitted As Variant
splitted = Split(valIn)
For i = LBound(splitted) To UBound(splitted)
If IsNumeric(splitted(i)) And Len(splitted(i)) = 5 Then
Debug.Print splitted(i)
End If
Next i
End Sub
Considering that in your example you mean that the 5 digit numbers are splitted by space, the above works. It splits the string by space to an array and loops through the elements of the array. If the element is with 5 chars and is numeric, it prints it.
If the rule for the spaces is not something that one can count on, here is a different implementation:
Sub TestMe()
Dim valIn As String
valIn = "44244rue4qsdqsdCURIE383201EYBENS43443"
Dim i As Long
For i = 1 To Len(valIn) - 4
If IsNumeric(Mid(valIn, i, 5)) Then
Debug.Print Mid(valIn, i, 5)
End If
Next i
End Sub
It starts looping through the string, checking whether each 5 chars are numeric. When you have numeric 6 chars, it gives two results - 1 to 5 and 2 to 6. Thus 383201 is "translated" as the following 2:
38320
83201
If you have always space between words/numbers then this should do
Sub test()
Dim TestStr As String
Dim Temp As Variant
Dim i As Long, FoundVal As Long
TestStr = "rue 4 qsdqsd CURIE 38320 EYBENS"
Temp = Split(TestStr, " ")
For i = 0 To UBound(Temp)
If Len(Trim(Temp(i))) = 5 And IsNumeric(Temp(i)) Then
FoundVal = Temp(i)
MsgBox FoundVal
End If
Next i
End Sub
From the solution you are trying to apply (creating custom function in VBA) I understand that you actually need to use it in a formula.
To find number with five digits from cell A1 you can use the following formula without VBA:
=IF(ISERROR(FIND("0"&REPT("1",5)&"0",CONCAT(0+(ISNUMBER(-MID(" "&A1&" ",ROW(INDIRECT("1:"&LEN(A1)+2)),1))+0)))),"",MID(A1,FIND("0"&REPT("1",5)&"0",CONCAT(0+(ISNUMBER(-MID(" "&A1&" ",ROW(INDIRECT("1:"&LEN(A1)+2)),1))+0))),5))
To search for other number of digits change the three occurrences of number 5 to your desired digits count in the formula.
With excel macro (VBA), I need to generate hash total for a series of strings. My current function cant output the correct result.
How to derive the hash total is as such:
Subtract the first 11 characters of originating account from the
first 11 characters of each receiving account.
If the account number is less than 11 characters, pad 0 on the right.
If the
account number has an alphabet, convert the alphabet to 0.
Derive the value for each receiving account. Take the absolute value, that
is, ignore the negative sign.
Add the absolute values
Take the first 11 character of the result. If the result is less
than 11 characters, pad with 0 on the left.
This is what I have now:
Function cleanString(text As String) As String
Dim output As String
Dim i As Integer
Dim c 'since char type does not exist in vba, we have to use variant type.
For i = 1 To Len(text)
'With Sheet2
MsgBox (i)
c = Mid(text, i, 1) 'Select the character at the i position
If (c >= "a" And c <= "z") Or (c >= "A" And c <= "Z") Then
output = output & "0" 'add the character to your output.
Else
output = output & c 'add the replacement character (space) to your output
End If
'End With
Next
cleanString = output
End Function
Function generateHash(LastRows As Long) As Double
Dim output As Double
'Dim c 'since char type does not exist in vba, we have to use variant type.
Dim Orig As String
Dim AccNo As String
Dim temp As Long
Orig = Left(Range("B3") & String(34, " "), 34)
For LastRows = 9 To LastRows
With Sheet2
AccNo = Left(.Cells(LastRows, 5) & String(11, " "), 11)
AccNo = cleanString(AccNo)
temp = Abs(AccNo - Orig)
output = output + temp
'MsgBox (output)
End With
Next
generateHash = output
End Function
These are the 26 sample strings, which should produce the hash total of "31341437052"
0039002572
0039002580
0030015769
0030016412
0259001090
0259001111
0039002637
0100703387
0100703395
0100703425
0100703433
0100703441
0100703450
0100703468
0100703476
0100703484
0011227958
0011228946
951382892
951700711
301402570
402705981
0030001620
0036001622
111111111
222222222
ok. so i figure out it was just some parameters that i was missing.
This convert any alphabet in the string to "0"
Function cleanString(text As String) As String
Dim output As String
Dim i As Integer
Dim c 'since char type does not exist in vba, we have to use variant type.
For i = 1 To Len(text)
'With Sheet2
'MsgBox (i)
c = Mid(text, i, 1) 'Select the character at the i position
If (c >= "a" And c <= "z") Or (c >= "A" And c <= "Z") Then
output = output & "0" 'add the character to your output.
Else
output = output & c 'add the replacement character (space) to your output
End If
'End With
Next
cleanString = output
'MsgBox (cleanString)
End Function
This start the hash function
Function generateHash(LastRows As Long) As Double
Dim output As Double
'Dim c 'since char type does not exist in vba, we have to use variant type.
Dim Orig As String
Dim AccNo As String
Dim temp As String
Dim lCounter As Long
Dim lLastRow As Long
'Find the last row that contains data
With Sheet2
lLastRow = .Cells(.Rows.Count, "A").End(xlUp).Row
End With
'Take the original string and making sure there are only 11 characters
Orig = Left(Range("B3") & "00000000000", 11)
'Starts from row 9
For lCounter = 9 To LastRows
With Sheet2
'Ensuring only 11 characters
AccNo = Left(.Cells(lCounter, 5) & "00000000000", 11)
'Clean the string of any alphabet
AccNo = cleanString(AccNo)
'Take the absolute diff of 2nd and 1st string
temp = Abs(AccNo - Orig)
output = output + temp
'MsgBox (output)
End With
Next
generateHash = output
End Function
hi i have this problem finding a string on a text box
so far this what i have it only detect the comma char, now i i input 23pm,24,25am how will i do this with this code or anybody can give me the simple code?
Dim tdates() As String
Dim numberOfDates, xcount As Integer
tdates = Split(TXTDAYS.Text, ",")
numberOfDates = UBound(tdates)
Dim counter As Integer
' loop through each input
For counter = 0 To numberOfDates
Dim xdate As String
xdate = LCase$(tdates(counter))
If Len(xdate) <= 2 Then
xcount = xcount + 1
Else
' if the original text has am or pm in it, add .5
If InStr(1, xdate, "am") > 0 Or InStr(1, xdate, "pm") > 0 Then
xcount = xcount + 0.5 'problem here it doesn't count
End If
End If
Next
if there is a better way to do this by detecting the comma and the am pm string much better.
Split the text on comma. Then your array will have all of the whole words in it
Use InStr to search for am or pm.
Replace AM and PM with "" and check the remainder of the text for a number (for validating)
' split the input on a comma.
dim dates() as String = Split(TXTDAYS.Text, ",")
dim numberOfDates as Integer = UBound(dates)
dim counter as Integer
' loop through each input
For counter = 0 to numberOfDates
dim dateEntered as String = LCase$(dates(counter))
' make sure the text entered is a number (once am and pm are removed)
dim dateNumber as String = Replace(Replace(dateEntered, "pm", ""), "am", "")
if IsNumeric(dateNumber) Then
COUNT = COUNT + 1
' if the original text has am or pm in it, add .5
if Instr(1, dateEntered , "am") > 0 Or Instr(1, dateEntered , "pm") > 0 Then
COUNT = COUNT + .5
end if
else
' do something to indicate invalid input
end if
Next
using instr()..
s = "admin#foo.com"
d = Mid(s, InStr(1, s, "#") + 1)
The variable d$ would end up with the string "foo.com". (Don't forget to check to make sure that the # sign is present, otherwise you would just end up with the whole source string.)
taken from this post..
VB6 Index of Substring
Thanks!
#leo