I'm trying to create a simple translation script that will look at a sentence in each cell from a range (column), and translate word by word based on the simple two-column (lookat/replace) translation memory I've created.
If the cell contains
"This app is cool"
and the translation memory is
This | 1
app | 2
cool | 3
Result should be:
"1 2 is 3"
However, using .Replace method, The below string:
"This apple from the cooler"
Would return
"1 2le from the 3er"
I used an array and split method to break the sentence into words, and then looked up at each words from my translation list for a xlwhole match. I have about 10,000 lines of sentences, and to break down each sentence into words would be roughly about 100,000 words and each word looking through about 1,000 list of translation words. It words.. but kind of slow.
Are there any other way, perhaps a better approach?
Here's another regex solution using the replace method and word boundaries (the "\b" in the regex pattern represents a word boundary). It assumes your source is in column A, and the results will go into column B.
The translation table is hard coded in the macro, but you could easily change that to be picked up from a table in your workbook.
Option Explicit
Sub Translate()
Dim V As Variant
Dim RE As Object
Dim arrTranslate As Variant
Dim I As Long, J As Long
Dim S As String
V = Range("a1", Cells(Rows.Count, "A").End(xlUp))
ReDim Preserve V(1 To UBound(V, 1), 1 To 2)
arrTranslate = VBA.Array(Array("This", 1), Array("app", 2), Array("cool", 3))
Set RE = CreateObject("vbscript.regexp")
With RE
.Global = True
.ignorecase = True
End With
For I = 1 To UBound(V, 1)
S = V(I, 1)
For J = 0 To UBound(arrTranslate)
RE.Pattern = "\b" & arrTranslate(J)(0) & "\b"
S = RE.Replace(S, arrTranslate(J)(1))
Next J
V(I, 2) = S
Next I
Range(Cells(1, 1), Cells(UBound(V, 1), UBound(V, 2))) = V
End Sub
Word to the rescue: Here I make use of of the "Match whole words only" option in Word's Find/Replace functionality.
Dim rngSentences As Range
Dim sentences, translatedSentences, wordsToReplace, newStrings
Dim iWord As Long
Dim iSentence As Long
Dim cell As Range
Dim w As Word.Application
Dim d As Word.Document
Set rngSentences = Range("A1:A5")
wordsToReplace = Array("this", "app", "cool")
newStrings = Array("1", "2", "3")
Set w = New Word.Application
Set d = w.Documents.Add(DocumentType:=wdNewBlankDocument)
sentences = rngSentences.Value ' read sentences from sheet
ReDim translatedSentences(LBound(sentences, 1) To UBound(sentences, 1), _
LBound(sentences, 2) To UBound(sentences, 2))
For iSentence = LBound(sentences, 1) To UBound(sentences, 1)
'Put sentence in Word document
d.Range.Text = sentences(iSentence, 1)
'Replace the words
For iWord = LBound(wordsToReplace) To UBound(wordsToReplace)
d.Range.Find.Execute Findtext:=wordsToReplace(iWord), _
Replacewith:=newStrings(iWord), MatchWholeWord:=True
Next iWord
'Grab sentence back from Word doc
translatedSentences(iSentence, 1) = d.Range.Text
Next iSentence
'slap translated sentences onto sheet
rngSentences.Offset(0, 1) = translatedSentences
w.Quit savechanges:=False
Another, potentially faster alternative is to paste all your sentences into the Word doc at once, replace everything, then copy-paste everything back to Excel sheet at once. It may be faster; I don't know, I haven't tested it extensively; up to you to do so.
To implement this, the lines after Set d = ... can be replaced with this:
'Copy-paste all sentences into Word doc
rngSentences.Copy
d.Range.PasteSpecial DataType:=wdPasteText
'Replace words
For iWord = LBound(wordsToReplace) To UBound(wordsToReplace)
d.Range.Find.Execute Findtext:=wordsToReplace(iWord), Replacewith:=newStrings(iWord), _
MatchWholeWord:=True
Next iWord
'Copy-paste back to Excel sheet
d.Range.Copy
rngSentences.Offset(0, 1).PasteSpecial xlPasteValues
w.Quit savechanges:=False
If you want you can use Regex.
Following the Scheme:
The Code:
' reference: "Microsoft VBScript Regular Expressions 5.5"
Dim RegX As Object, Mats As Object, Counter As Long
Set RegX = CreateObject("VBScript.RegExp")
Dim TrA(1 To 1000) As String
Dim TrB(1 To 1000) As String
Dim TrMax As Integer
Dim StrSp
For i = 1 To 9999
If Range("D" & i).Value = "" Then Exit For
TrA(i) = Range("D" & i).Value
TrB(i) = Range("E" & i).Value
TrMax = i
Next
Range("B1:B10").ClearContents
For i = 1 To 9999
If Range("A" & i).Value = "" Then Exit For
With RegX
.Global = True
.Pattern = "[a-zA-Z0-9]+"
Set Mats = .Execute(Range("A" & i).Value)
End With
kk = Range("A" & i).Value
For Counter = 0 To Mats.Count - 1
For e = 1 To TrMax
If LCase(Mats(Counter)) = TrA(e) Then
kk = Replace(kk, Mats(Counter), TrB(e), , 1)
End If
Next
Next
Range("B" & i).Value = kk
Next
Set Mats = Nothing
Set RegX = Nothing
Regex it's quickly, but the Word code it's very interesting (Copy & Paste ... :-)
Related
Hello smart human beings out there
I have this setup in my Excel
Basically, what I'm trying to achieve here is automatically grab every single string from column A (and paste to column H) and return the frequency in column I. The script is below
Sub WordCountTester()
Dim d As Object, k, i As Long, ws As Worksheet
Set ws = ActiveSheet
With ws.ListObjects("Table3")
If Not .DataBodyRange Is Nothing Then
.DataBodyRange.Delete
End If
End With
Set d = WordCounts(ws.Range("A2:A" & ws.Cells(Rows.Count, "A").End(xlUp).Row), _
ws.Range("F2:F" & ws.Cells(Rows.Count, "F").End(xlUp).Row))
'list words and frequencies
For Each k In d.keys
ws.Range("H2").Resize(1, 2).Offset(i, 0).Value = Array(k, d(k))
i = i + 1
Next k
End Sub
'rngTexts = range with text to be word-counted, defined in set d= above
'rngExclude = 'range with words to exclude from count, defined in set d= above
Public Function WordCounts(rngTexts As Range, rngExclude As Range) As Object 'dictionary
Dim words, c As Range, dict As Object, regexp As Object, w, wd As String, m
Set dict = CreateObject("scripting.dictionary")
Set regexp = CreateObject("VBScript.RegExp") 'see link below for reference
With regexp
.Global = True
.MultiLine = True
.ignorecase = True
.Pattern = "[\dA-Z-]{3,}" 'at least 3 characters
End With
'loop over input range
For Each c In rngTexts.Cells
If Len(c.Value) > 0 Then
Set words = regexp.Execute(LCase(c.Value))
'loop over matches
For Each w In words
wd = w.Value 'the text of the match
If Len(wd) > 1 Then 'EDIT: ignore single characters
'increment count if the word is not found in the "excluded" range
If IsError(Application.Match(wd, rngExclude, 0)) Then
dict(wd) = dict(wd) + 1
End If
End If '>1 char
Next w
End If
Next c
Set WordCounts = dict
End Function
However, it currently count the string with 1 word only. I want to count strings with 2 and 3 words (and I will consider drive-by as 2 words). Can someone please tell me where in this code I have to fix to achieve that? I still want to keep column F there because there can be 2- or 3- word strings that I want to exclude. Thanks!
If you changed your mind and consider that also two words pairs 2-3, 4-5, 6-7 and so on are necessary, please test the next solution:
Private Sub WordPairsCountTester()
Dim d As Object, k, i As Long, ws As Worksheet, arrFin
Set ws = ActiveSheet
'Attention, please! The last parameter of the called function means How Many Consecutive Words to be counted
Set d = WordPairCountsSp(ws.Range("A2:A" & ws.cells(rows.count, "A").End(xlUp).row), _
ws.Range("F2:F" & ws.cells(rows.count, "F").End(xlUp).row), 3)
arrFin = Application.Transpose(Array(d.Keys, d.items)) 'place the dictionary in an array
'clear contents of the columns where a previous result was returned, if any...:
ws.Range("H2:I" & ws.Range("H" & ws.rows.count).End(xlUp).row).ClearContents
ws.Range("H2").Resize(UBound(arrFin), 2).Value = arrFin 'drop the array content at once
End Sub
Private Function WordPairCountsSp(rngTexts As Range, rngExclude As Range, nrNeigh As Long) As Object
Dim dict As Object, arr, arrCell, i As Long, pairWd As String, j As Long, k As Long
arr = rngTexts.Value 'place the range in an array for faster iteration
Set dict = CreateObject("scripting.dictionary")
For i = 1 To UBound(arr) 'iterate between the array elements
arrCell = Split(Replace(Replace(Replace(Replace(arr(i, 1), ",", ""), ".", ""), "?", ""), "!", "")) 'split the string by default delimiter (space)
If UBound(arrCell) + 1 >= nrNeigh Then
For j = 0 To UBound(arrCell) - nrNeigh + 1 'iterate between the array elements
pairWd = arrCell(j)
For k = 1 To nrNeigh - 1
pairWd = pairWd & " " & arrCell(j + k) 'create a string from nrNeigh neighbour words
Next k
If IsError(Application.match(pairWd, rngExclude, 0)) Then
dict(pairWd) = dict(pairWd) + 1 'place the unique pairs as keys and add occurrences as items
End If
Next j
End If
Next i
Set WordPairCountsSp = dict 'return the above created dictionary
End Function
I have the VB code below
Sub CountWordFrequencies()
Dim InputSheet As Worksheet
Dim WordListSheet As Worksheet
Dim PuncChars As Variant, x As Variant
Dim i As Long, r As Long, b As Long
Dim txt As String
Dim wordCnt As Long
Dim AllWords As Range
Dim PC As PivotCache
Dim PT As PivotTable
Dim PF As PivotField
Application.ScreenUpdating = False
Set InputSheet = ActiveSheet
Set WordListSheet = Worksheets.Add(after:=Worksheets(Sheets.Count))
WordListSheet.Range("A1").Font.Bold = True
WordListSheet.Range("A1") = "All Words"
InputSheet.Activate
wordCnt = 2
PuncChars = Array(".", ",", ";", ":", "'", "!", "#", _
"$", "%", "&", "(", ")", " - ", "_", "--", "+", _
"=", "~", "/", "\", "{", "}", "[", "]", """", "?", "*")
r = 2
Dim NotRealWord As Variant
NotRealWord = Array("OF","THE")
Do While Cells(r, 1) <> ""
txt = UCase(Cells(r, 1))
For i = 0 To UBound(PuncChars)
txt = Replace(txt, PuncChars(i), "")
Next i
txt = WorksheetFunction.Trim(txt)
x = Split(txt)
For i = 0 To UBound(x)
WordListSheet.Cells(wordCnt, 1) = x(i)
wordCnt = wordCnt + 1
Next i
r = r + 1
Loop
WordListSheet.Activate
Set AllWords = Range("A1").CurrentRegion
Set PC = ActiveWorkbook.PivotCaches.Add _
(SourceType:=xlDatabase, _
SourceData:=AllWords)
Set PT = PC.CreatePivotTable _
(TableDestination:=Range("C1"), _
TableName:="PivotTable1")
With PT
.AddDataField .PivotFields("All Words")
.PivotFields("All Words").Orientation = xlRowField
.PivotFields("All Words") _
.AutoSort xlDescending, "Count of All Words"
End With
Set PF = ActiveSheet.PivotTables("PivotTable1").PivotFields("All Words")
With PF
.ClearManualFilter
.EnableMultiplePageItems = True
For b = LBound(NotRealWord) To UBound(NotRealWord)
.PivotItems(NotRealWord(b)).Visible = False
Next b
End With
End Sub
This one is a Word Frequency Analysis function where the user will insert the list of strings in column A, starting from A2. They will click a button that run this script. The script will then break the strings into single words and create a pivot table that will count the frequency of each word, sorted by the frequency.
Here are the pictures showing the mechanism:
Input from user on column A
Result
The result
Now I have an issue with the filter. Ultimately, I want the pivot table to automatically filter out the list of words in the "NotRealWord" array because these are not useful words to analyze. My code works only when the script can find all values in the array list in the words being broken out. So in my example, I set NotRealWord = Array("OF", "THE") and the pivot table field does have these words so it works perfectly. But if I added "BY", it returns this error "Unable to get the PivotItems property of the PivotField class". How do I fix this?
Or even better, how can I make NotRealWord a dynamic array which takes the list of values in let's say column F so that the user can add in more words that they want to filter out without having to fix the code (my first picture also shows that column F).
Please note that I'm not super good at VB. I know how to read and adapt complicated codes but don't know the in and out of FB word
Here's one possible approach which is a little different from your current one but should do what you want:
Sub WordCountTester()
Dim d As Object, k, i As Long, ws As Worksheet
Set ws = ActiveSheet
Set d = WordCounts(ws.Range("A2:A" & ws.Cells(Rows.Count, "A").End(xlUp).Row), _
ws.Range("F2:F" & ws.Cells(Rows.Count, "F").End(xlUp).Row))
'list words and frequencies
For Each k In d.keys
ws.Range("H2").Resize(1, 2).Offset(i, 0).Value = Array(k, d(k))
i = i + 1
Next k
End Sub
'rngTexts = range with text to be word-counted
'rngExclude = range with words to exclude from count
Public Function WordCounts(rngTexts As Range, rngExclude As Range) As Object 'dictionary
Dim words, c As Range, dict As Object, regexp As Object, w, wd As String, m
Set dict = CreateObject("scripting.dictionary")
Set regexp = CreateObject("VBScript.RegExp") 'see link below for reference
With regexp
.Global = True
.MultiLine = True
.ignorecase = True
.Pattern = "[\dA-Z-]{2,}" 'at least two characters...
End With
'loop over input range
For Each c In rngTexts.Cells
If Len(c.Value) > 0 Then
Set words = regexp.Execute(UCase(c.Value))
'loop over matches
For Each w In words
wd = w.Value 'the text of the match
If Not IsNumeric(wd) Then 'EDIT: ignore numbers
'increment count if the word is not found in the "excluded" range
If IsError(Application.Match(wd, rngExclude, 0)) Then
dict(wd) = dict(wd) + 1
End If
Else
Debug.Print "Filtered out", wd 'excluding numbers...
End If '>1 char
Next w
End If
Next c
Set WordCounts = dict
End Function
Regular expressions reference: https://learn.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/scripting-articles/ms974570(v=msdn.10)
I have an excel sheet that has cells with variable amounts of line breaks and I want to reduce it so that there is only one line break between each new line.
For example
HELLO
WORLD
GOODBYE
will be modified to:
HELLO
WORLD
GOODBYE
I've been banging my head over this for hours and have come up with a few ways but none are very efficient or produce the best results.
This is made especially difficult because I'm working with a dataset that has spaces preceeding the Line Breaks.
And so a regular parse doesn't work as well.
I've tried to replace all the instances of chr(10) in the cell with ~ to make it easier to work with, however i'm still not getting it to an exact amount. I'm wondering if there are better ways.
here is what I have so far:
myString = Replace(myString, Chr(10), "~")
Do While InStr(myString, "~~") > 0
str1 = Split(myString, "~")
For k = 0 To UBound(str1)
myString = Replace(myString, "~~", "~")
Next k
Loop
Do While InStr(myString, " ~") > 0
str1 = Split(myString, "~")
For k = 0 To UBound(str1)
myString = Replace(myString, " ~", "")
Next k
Loop
myString = Replace(myString, " ~", " ~")
myString = Replace(myString, " ~", "~")
myString = Replace(myString, "~", Chr(10))
Cells(2, 2).Value = myString
So i'm using a few do while loops to catch instances of different types of line breaks (or in this case, tildes) but I don't think this is the best way to tackle this.
I was thinking of ways to loop through the characters in the cell, and if there is an instance where there is more than one chr(10), replace it with "".
So the psuedocode would look like:
for i to len(mystring)
if mystring(i) = chr(10) AND myString(i+1) = chr(10) Then
myString(i + 1) = ""
but unfortunately I don't think this is possible through vba.
If anyone is kind enough to help me adjust my current code or assist me with the aforementioned psuedocode, it would be greatly appreciated!
You can do it with a formula:
=SUBSTITUTE(SUBSTITUTE(TRIM(SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(TRIM(A1)," ","|"),"|"&CHAR(10)," "),CHAR(10)," "))," ",CHAR(10)),"|"," ")
This changes all the spaces to | and then the Char(10) to spaces. The trim removes the extra spaces. The we reverse, space to Char(10) and | to spaces.
VBA:
Function manytoone(str As String)
str = Replace(Application.Trim(str), " ", "|")
str = Replace(str, "|" & Chr(10), " ")
str = Replace(str, Chr(10), " ")
str = Application.Trim(str)
str = Replace(str, " ", Chr(10))
str = Replace(str, "|", " ")
manytoone = str
End Function
You can use Regular Expressions.
The regex pattern below removes any line that contains zero to any number of spaces, along with its terminating crlf, and also removes the crlf at the end of the final word.
Option Explicit
Sub trimXSLF()
Dim myRng As Range, myCell As Range, WS As Worksheet
Dim RE As Object
Const sPat As String = "^\s*[\x0A\x0D]+|[\x0A\x0D](?!\s*\S+\s*)"
Const sRepl As String = ""
Set WS = Worksheets("sheet4") 'or whatever
With WS
Set myRng = .Range(.Cells(1, 1), .Cells(.Rows.Count, 1).End(xlUp))
End With
Set RE = CreateObject("vbscript.regexp")
With RE
.Global = True
.MultiLine = True
.Pattern = sPat
For Each myCell In myRng
myCell = .Replace(myCell.Value2, sRepl)
Next myCell
End With
End Sub
If myRng is large (tens of thousands of rows), the macro could run the process over a VBA array for speed.
A VBA method would be replacing consecutive vbLf constants with a single one.
Loop through the string as long as there are more than one vbLf together, once removed, replace the string.
Sub RemoveExcessLinebreaks()
Dim s As String, rng As Range
Set rng = ThisWorkbook.Worksheets(1).Range("B4")
s = rng.Value
While InStr(1, s, vbLf & vbLf) > 0
s = Replace(s, vbLf & vbLf, vbLf)
Wend
rng.Value = s
End Sub
Obviously, you would need to modify the rng object to your purposes, or turn it into a parameter to the sub itself.
vbLf is a constant for a "LineFeed". There are multiple types of new lines, such as a vbCr (Carriage Return) or a vbCrLf (combined). Pressing Alt + Enter in a cell appears to use the vbLf variant, which is why I used this constant over the others.
This has already been answered fairly well, but not meeting one of the requirements yet (have 1 line between each new line), so here is my take on answering this. Please see the comments through the code for more details:
Option Explicit
Sub reduceNewLines()
Dim ws As Worksheet: Set ws = ActiveWorkbook.Sheets("Sheet1")
Dim lRow As Long: lRow = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row
Dim lCol As Long: lCol = ws.Cells(1, ws.Columns.Count).End(xlToLeft).Column
Dim arrData As Variant: arrData = ws.Range(ws.Cells(1, 1), ws.Cells(lRow, lCol))
Dim arrVal() As String
Dim R As Long, C As Long, X As Long
For R = LBound(arrData) To UBound(arrData) 'Iterate through each row of data
For C = LBound(arrData, 2) To UBound(arrData, 2) 'iterate through each column of data (though might be just 1)
arrVal = Split(arrData(R, C), Chr(10)) 'allocate each row to an array, split at new line
arrData(R, C) = "" 'reset the data inside this field
For X = LBound(arrVal) To UBound(arrVal)
arrVal(X) = Trim(arrVal(X)) 'clear leading/trailing spaces
If Left(arrVal(X), 1) <> " " And arrVal(X) <> "" Then
arrData(R, C) = arrData(R, C) & arrVal(X) & Chr(10) & Chr(10) 'allocate new data + 2 lines
End If
Next X
arrData(R, C) = Left(arrData(R, C), Len(arrData(R, C)) - 2) 'remove the last 2 extra new lines
Next C
Next R
ws.Range(ws.Cells(1, 1), ws.Cells(lRow, lCol)) = arrData 'allocate the data back to the sheet
End Sub
Happy to assist further if needed.
Disclaimer- my case is specific, and in my case my code works because I know the pattern.
I was looking for an answer everywhere, and the codes I tried were not quite what I was looking for, this is my solution if you are looking for a set of numbers.
In my case, I was looking for 7 digits, starting with digit 1 in a a column with random strings, some string had the number some others didn't.
The number will appear in these three scenarios "1XXXXXX", "PXXXXXXXX", "PXXXXXXXXX"(this has more digits because there is a slash).
Here are the examples of strings:
9797 P/O1743061 465347 Hermann Schatte Earl Lowe
9797 Po 1743071 404440 Claude Gaudette Jose Luis Lopez
9817 1822037 463889 Jean Caron Mickelly Blaise
My Code
Sub getnum()
'i don't use explicit so i didn't declare everything
Dim stlen As String
Dim i As Integer
Dim arra() As String
Dim arran() As String
Orig.AutoFilterMode = False
Call BeginMacro
LastRow = Orig.Cells(Rows.Count, 1).End(xlUp).Row
Orig.Range("J2:J" & LastRow).Clear
'loop though column
For n = 2 To LastRow
celref = Orig.Cells(n, 4).Value
'split string on white spaces
arra() = Split(celref, " ")
'turn string to multiple strings
For counter = LBound(arra) To UBound(arra)
strin = arra(counter)
'remove white spaces from string
storage = Trim(strin)
lenof = Len(storage)
'if string has 9 characthers, check for conditions
If lenof = 9 Then
'position of first and last charachter
somstr = Mid(storage, 1, 1)
somot = Mid(storage, 9, 1)
If somstr = "P" Or somstr = "p" And IsNumeric(somot) = True Then
'removes Po or PO and keeps only 7 digits
storage = Right(storage, 7)
'stores in column J
Orig.Cells(n, 10).Value = storage
End If
ElseIf lenof = 10 Then
somstr = Mid(storage, 1, 1)
somot = Mid(storage, 10, 1)
'other conditions
If somstr = "P" Or somstr = "p" And IsNumeric(somot) = True Then
'removes Po or PO and keeps only 7 digits
storage = Right(storage, 7)
'stores in column J
Orig.Cells(n, 10).Value = storage
End If
End If
'eliminate comma within
arran() = Split(storage, ",")
If Orig.Cells(n, 10).Value <> storage Then
For counter2 = LBound(arran) To UBound(arran)
strin2 = arran(counter2)
storage2 = Trim(strin2)
'final condition if is 7 digits and starts with 1
If IsNumeric(storage2) = True And Len(storage2) = 7 Then
car = Mid(storage2, 1, 1)
If car = 1 Then
'stores in columns J at specific position
Orig.Cells(n, 10).Value = storage2
End If
Else
If isnumeric(orig.cells(n,10).value) =true and _
len(orig.cells(n,10).value = 7 then
orig.cells(n,10).value = orig.cells(n,10).value
else
Orig.Cells(n, 10).Value = "no po# in D"
End If
Next counter2
End If
Next counter
Next n
Call EndMacro
End Sub
you may try this
Option Explicit
Sub getnum()
Dim position As Variant
Dim cell As Range
With Worksheets("Orig") ' change it to your actual sheet name
With Intersect(.UsedRange, Columns("J"))
.Replace what:="P/O", replacement:="P/O ", lookat:=xlPart
For Each cell In .Cells
position = InStr(cell.Text, " 1")
If position > 0 Then cell.Value = Mid(cell.Value, position + 1, 7)
Next
End With
End With
End Sub
This code paste two formulas one in column G and one in column J). The first formula checks for a "P" in the first character of the cell in column 2 and if there is a "P" it extracts the last 7 characters in the string and puts them in column G. The second formula checks if there is not a "P" and if not extracts the last 7 characters in the string and puts them in column J.
Sub Extract()
Dim ws As Worksheet
Dim lRow As Long
Set ws = ThisWorkbook.Sheets("Sheet3")
lRow = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
ws.Range("G2:G" & lRow).Formula = "=IF(LEFT(B2)=""P"",(RIGHT(B2,7)),"""")"
ws.Range("J2:J" & lRow).Formula = "=IF(LEFT(B2)<>""P"",(RIGHT(B2, 7)),"""")"
End Sub
You may use the RegEx to extract the number in desired format.
Please give this a try...
Function Get10DigitNumber(ByVal Str As String) As String
Dim RE As Object
Set RE = CreateObject("VBScript.RegExp")
With RE
.Global = False
.Pattern = "1\d{6}"
End With
If RE.test(Str) Then
Get10DigitNumber = RE.Execute(Str)(0)
End If
End Function
Then if you want to use this function on the worksheet itself, assuming your string is in A2, try this...
=Get10DigitNumber(A2)
OR
You may use this function in another sub routine/macro like this...
Debug.Print Get10DigitNumber(<pass your string variable here>)
Edited Function:
Function Get10DigitNumber(ByVal Str As String) As String
Dim RE As Object, Matches As Object
Set RE = CreateObject("VBScript.RegExp")
With RE
.Global = False
.Pattern = "[Pp]?\/?[Oo]?(1\d{6})\b"
End With
If RE.test(Str) Then
Set Matches = RE.Execute(Str)
Get10DigitNumber = Matches(0).SubMatches(0)
End If
End Function
And use if as already described above.
After understanding what you were doing, I think this will work. Any feedback would be appreciated.
Dim cell As Range, LRow As Long
LRow = ActiveSheet.Cells(Rows.Count, 4).End(xlUp).Row
For Each cell In Range("D2:D" & LRow)
If cell.Value Like "*Po *" Then
cell.Offset(0, 6).Value = Split(cell.Value, " ")(2)
Else: cell.Offset(0, 6).Value = Split(cell.Value, " ")(1)
End If
Next cell
For Each cell In Range("J2:J" & LRow)
If Len(cell.Value) > 7 Then
cell.Value = Right(cell.Value, 7)
End If
Next
I'm trying to clean names on two separate sheets "Alpha Roster" and "Paid". Alpha Roster is updated by other people and Paid is my master tracker of who has paid. I have a function called "MakeProper" that works fairly well at making corrections on Alpha Roster but for some reason does not make any corrections to Paid. Both sheets are set up the same.
Sub CleanUpPaid()
Sheets("Paid").Activate
Sheets("Paid").Select
Range("A2").Select
MakeProper
End Sub
Sub MakeProper()
Dim rngSrc As Range
Dim lMax As Long, lCtr As Long
Set rngSrc = ActiveSheet.Range(ActiveWindow.Selection.Address)
lMax = rngSrc.Cells.Count
' clean up Sponsor's Names
For lCtr = 3 To lMax
If Not rngSrc.Cells(lCtr, 1).HasFormula And _
rngSrc.Cells(lCtr, 1) <> "CMC" Then
rngSrc.Cells(lCtr, 1) = MakeBetterProper(rngSrc.Cells(lCtr, 1))
End If
' clean up Guest's Names
If Not rngSrc.Cells(lCtr, 7).HasFormula Then
rngSrc.Cells(lCtr, 7) = MakeBetterProper(rngSrc.Cells(lCtr, 7))
End If
Next lCtr
'MsgBox ("Make Proper " & ActiveSheet.Name)
End Sub
Function MakeBetterProper(ByVal ref As Range) As String
Dim vaArray As Variant
Dim c As String
Dim i As Integer
Dim J As Integer
Dim vaLCase As Variant
Dim str As String
' Array contains terms that should be lower case
vaLCase = Array("CMC", "II", "II,", "III", "III,")
ref.Replace what:=",", Replacement:=", "
ref.Replace what:=", ", Replacement:=", "
ref.Replace what:="-", Replacement:=" - "
c = StrConv(ref, 3)
'split the words into an array
vaArray = Split(c, " ")
For i = (LBound(vaArray) + 1) To UBound(vaArray)
For J = LBound(vaLCase) To UBound(vaLCase)
' compare each word in the cell against the
' list of words to remain lowercase. If the
' Upper versions match then replace the
' cell word with the lowercase version.
If UCase(vaArray(i)) = UCase(vaLCase(J)) Then
vaArray(i) = vaLCase(J)
End If
Next J
Next i
' rebuild the sentence
str = ""
For i = LBound(vaArray) To UBound(vaArray)
str = str & " " & vaArray(i)
str = Replace(str, " - ", "-")
str = Replace(str, "J'q", "J'Q")
str = Replace(str, "Jr", "Jr.")
str = Replace(str, "Jr..", "Jr.")
str = Replace(str, "(Jr.)", "Jr.")
str = Replace(str, "Sr", "Sr.")
str = Replace(str, "Sr..", "Sr.")
Next i
MakeBetterProper = Trim(str)
End Function
I read up on the difference between select and activate. As you can see, in CleanUpPaid, I try a couple different ways to make the Paid sheet the active sheet but nothing appears to occur on the sheet like it does in Alpha Roster.
You are only processing one cell on the Worksheets("Paid") and that is Range("A2"). You can eleiminate Set rngSrc = ActiveSheet.Range(ActiveWindow.Selection.Address) and just use Selection it is returning a range object.
Assuming that you want to process the cells in columns A and G. I'm using my function TitleCase to correct the capitalization but you can substitute MakeBetterProper if you would like.
Sub FixNames()
Application.ScreenUpdating = False
Dim ws As Worksheet
Dim c As Range
For Each ws In Worksheets(Array("Alpha Roster", "Paid"))
With ws
For Each c In Intersect(.Columns(1), .UsedRange)
If Not c.HasFormula And c.Value <> "CMC" Then c.Value = TitleCase(c.text)
Next
For Each c In Intersect(.Columns(7), .UsedRange)
If Not c.HasFormula Then c.Value = TitleCase(c.text)
Next
End With
Next
Application.ScreenUpdating = True
End Sub
My answer to How to make every letter of word into caps but not for letter “of”, “and”, “it”, “for” ?. will correct the capitalization for you.
I used Rules for Capitalization in Titles of Articles as a reference to create a capitalization exceptions list.
Function TitleCase uses WorksheetFunction.ProperCase to preproccess the text. For this reason, I put in an exception for contractions because WorksheetFunction.ProperCase improperly capitalizes them.
The first word in each sentence and the first word after a double quotation mark will remain capitalized. Punctuation marks are also handled properly.
Function TitleCase(text As String) As String
Dim doc
Dim sentence, word, w
Dim i As Long, j As Integer
Dim arrLowerCaseWords
arrLowerCaseWords = Array("a", "an", "and", "as", "at", "but", "by", "for", "in", "of", "on", "or", "the", "to", "up", "nor", "it", "am", "is")
text = WorksheetFunction.Proper(text)
Set doc = CreateObject("Word.Document")
doc.Range.text = text
For Each sentence In doc.Sentences
For i = 2 To sentence.Words.Count
If sentence.Words.Item(i - 1) <> """" Then
Set w = sentence.Words.Item(i)
For Each word In arrLowerCaseWords
If LCase(Trim(w)) = word Then
w.text = LCase(w.text)
End If
j = InStr(w.text, "'")
If j Then w.text = Left(w.text, j) & LCase(Right(w.text, Len(w.text) - j))
Next
End If
Next
Next
TitleCase = doc.Range.text
doc.Close False
Set doc = Nothing
End Function