Instr function only on specific font - excel

I´m trying to find a way to use Instr to work only with words that have a specific font.
I´m currently using a code that allows me to find differences between two paragraphs and show the changes on another column by chainging the words that are the same to the color green.
The problem is that when using Instr it only finds the first occurence of a word. But with the paragraphs I´m using, the words appear multiple times:
myLastRow = Cells(Rows.Count, "G").End(xlUp).Row
For I = 3 To myLastRow
strTemp = " "
WordsA = Split(Range("F" & I).Text, " ")
Debug.Print WordsA
WordsB = Split(Range("H" & I).Text, " ")
Debug.Print WordsB
For ndxB = LBound(WordsB) To UBound(WordsB)
For ndxA = LBound(WordsA) To UBound(WordsA)
If StrComp(WordsA(ndxA), WordsB(ndxB), vbTextCompare) = 0 Then
FindText = WordsA(ndxA)
Debug.Print FindText
Set TextRange = Range("H" & I)
fontColor = 4
'FindText.Font.ColorIndex = fontColor
For Each part In TextRange
lenOfpart22 = InStr(1, TextRange, FindText, 1)
lenPart = Len(FindText)
part.Characters(Start:=lenOfpart22, Length:=lenPart).Font.ColorIndex = fontColor
Next part
Exit For
End If
Next ndxA
Next ndxB
Next I
What I need is for the Instr to only search the word if its fond is 0 (black).
TextRange is the paragraph. Usually more than 500 caracters long
FindText is the word that I´m searching
This is an example of the problem I´m having:
In this paragraph you can see how some words appear in green. These are the words that are the same on the two paragraphs that I´m comparing (columns F and G). There are some words such as: aeqqw, SAWR, SIGMEL... that are different. The problem is that Instr only finds the first occurrence of a word. That´s why I want a condition were if the word is green, it won´t be considered in the instr and will move on to find the next word.
In the picture you can see that the first "El" is in green, but the rest aren´t. This is because when it searches for the second, thrid, fourth... "el" it comes back to the first "el".

Please, use the next function to do what (I understood) you need (playing with arrays...):
Sub WordsComp(cell1 As Range, cell2 As Range) 'punctuation characters eliminated
Dim arr1() As String, arr2() As String, arrMtch() As String, mtch, El
Dim strArr As String, i As Long, cleanWord As String, endPlus As Long
arr1 = Split(cell1.value): arr2 = Split(cell2.value) 'split the two cells content by words
For Each El In arr1 'iterate between the first cell words
For i = 0 To UBound(arr2)
cleanWord = EndingCharsOut(CStr(El))
endPlus = Len(cleanWord) - Len(El)
If EndingCharsOut(CStr(arr2(i))) = cleanWord Then 'when a match has been found:
arrMtch = Split(cell2, , i + 1, vbTextCompare) 'split the range only up to the searched word (plus the rest of the string)
'eliminate the last element of the array:
arrMtch(UBound(arrMtch)) = "##$%": arrMtch = filter(arrMtch, "##$%", False)
strArr = Join(arrMtch, "|") 'join the array elements to obtain the necessary start, before the word to be colored
cell2.Characters(start:=Len(strArr) + 2, length:=Len(El) + endPlus).Font.Color = vbGreen '+ 2 because of the 1D zero based array and a space
End If
Next i
Next
End Sub
Private Function EndingCharsOut(strMatch As String) As String 'eliminates ending punctuation characters (,.?:;)
With CreateObject("Vbscript.RegExp")
.Pattern = "[.,/?:;]$"
If .test(strMatch) Then
EndingCharsOut = (.Replace(strMatch, ""))
Else
EndingCharsOut = strMatch
End If
End With
End Function
The above Sub should be called by the next one:
Sub testWordsCompare()
Dim ws As Worksheet, rng As Range, lastR As Long, i As Long
Set ws = ActiveSheet
lastR = ws.Range("F" & ws.rows.count).End(xlUp).row
Set rng = ws.Range("F2:G" & lastR)
rng.Columns(2).Font.Color = 0 'make the font color black (default)
Application.EnableEvents = False: Application.ScreenUpdating = False
For i = 1 To rng.rows.count
WordsComp rng.rows(i).cells(1, 1), rng.rows(i).cells(1, 2)
Next i
Application.EnableEvents = True: Application.ScreenUpdating = True
MsgBox "Ready..."
End Sub
The function compares words even containing punctuation (comma, dot, question mark, ":", ";") at the end...
A faster solution but not so compact and easy to be understood, would be the next classic one:
Sub compWdClassic(cell1 As Range, cell2 As Range)
Dim iStart1 As Long, iEnd1 As Long, iStart2 As Long, oldStart As Long, strWd As String
Dim boolEnd As Boolean, boolOut As Boolean, i As Long, frstW As Boolean, midleW As Boolean
iStart1 = 1 'initialize starting position for Cell1 string
Do While Not boolEnd
iEnd1 = InStr(iStart1, cell1, " ", vbBinaryCompare) 'determine the ending of the word to be returned
strWd = Mid(cell1, iStart1, IIf(iEnd1 > 0, iEnd1 - iStart1, Len(cell1) - iStart1 + 1)) ' extraxting the word to be checked
If iEnd1 > 0 Then iStart1 = iEnd1 + 1 Else: boolEnd = True 'determine if is it about the last word (or not)...
strWd = EndingCharsOut(strWd) 'clean the word ending
midleW = False: boolOut = False: iStart2 = 1 'initialize the necessary variables
Do While Not boolOut 'loop in cell2 value string
If Not frstW And iStart2 = 1 Then 'if not a first word has been found:
iStart2 = InStr(IIf(iStart2 = 0, 1, iStart2), cell2, strWd & " ", vbBinaryCompare) 'check against a word without a space in front
If iStart2 > 0 Then frstW = True 'first word in the sentence. If such a word found, make the boolean variable True
Else
oldStart = iStart2 'memorize the previous value of iStart2
iStart2 = InStr(iStart2 + 1, cell2, " " & strWd & " ", vbBinaryCompare) 'search if a word with spaces at both sides
If iStart2 > 0 Then midleW = True 'if founded, make the boolean variable True
If oldStart > 0 And midleW Then 'if nothing found before, but a pevious word with spaces of both sides has been found:
If iStart2 = 0 Then iStart2 = InStr(oldStart, cell2, " " & strWd, vbBinaryCompare): _
If iStart2 > 0 And iStart2 + Len(strWd) = Len(cell2) Then boolOut = True Else: iStart2 = 0: boolOut = True: 'if the last word or only part of a word
ElseIf oldStart = 0 And Not midleW Then
If iStart2 = 0 Then iStart2 = InStr(oldStart + 1, cell2, " " & strWd, vbBinaryCompare):
If iStart2 > 0 Then boolOut = True: ' last word and loop must be exited
End If
End If
If iStart2 > 0 Then
cell2.Characters(iStart2 + IIf(boolOut, 1, IIf(frstW And Not midleW, 0, 1)), Len(strWd)).Font.Color = vbRed 'do the job
iStart2 = iStart2 + Len(strWd) + 1 'increment the variable for the next search
Else
If (frstW And Not boolOut) Or (Not frstW And Not midleW And Not boolOut) Then Exit Do 'exiting loop if conditions are met
End If
Loop
Loop
End Sub
It uses the same EndingCharsOut function to clear punctuation characters. You only must call this Sub instead of previous. I mean, replace:
WordsComp rng.rows(i).cells(1, 1), rng.rows(i).cells(1, 2)
in testWordsCompare sub with:
compWdClassic rng.rows(i).cells(1, 1), rng.rows(i).cells(1, 2)
Please, send some feedback after testing them...

Related

Check for similarities within a single cell

Does anyone knows how to higlight similar words within a single cell?
What I want to achieve is to find duplicates/similar words between < ; > in a cell.
example cell:
home;music;car;window;musician
desired result:
music in a word musician is hilglighted or the output is 'TRUE' in the next column if similar word like this has been found.
I was able to compare words between two columns but not within single cell.
So far I was able to create a formula to separate the words in the cell to have each range separate to work with.
Function IdDuplicates(rng As Range) As String
Dim StringtoAnalyze As Variant
Dim I As Integer
Dim J As Integer
Const minWordLen As Integer = 4
StringtoAnalyze = Split(UCase(rng.Value), ";")
For I = UBound(StringtoAnalyze) To 0 Step -1
If Len(StringtoAnalyze(I)) < minWordLen Then GoTo SkipA
For J = 0 To I - 1
If StringtoAnalyze(J) = StringtoAnalyze(I) Then
IdDuplicates = "TRUE"
GoTo SkipB
End If
Next J
SkipA:
Next I
IdDuplicates = "FALSE"
SkipB:
End Function
Any ideas?
This function returns either FALSE or a list of the similar words
Option Explicit
Function IdDuplicates(rng As Range) As String
Dim s As String, word, m As Object, i As Long
With CreateObject("VBScript.RegExp")
.IgnoreCase = True
.Global = True
s = Trim(rng.Value2)
For Each word In Split(s, ";")
.Pattern = word
Set m = .Execute(s)
If m.Count > 1 Then
IdDuplicates = IdDuplicates & "," & word
End If
Next
End With
If IdDuplicates = "" Then
IdDuplicates = "FALSE"
Else
IdDuplicates = Mid(IdDuplicates, 2) ' remove lead ,
End If
End Function
Match Partially in Delimited String
Option Explicit
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Purpose: Returns a boolean indicating whether any of the substrings
' in a delimited string is contained in another substring.
' Example: 'music;car;musician' - 'music' is found in 'musician' - TRUE
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function MatchPart( _
ByVal DelimitedString As Variant, _
Optional ByVal StringDelimiter As String = ";") _
As Boolean
Dim S As String: S = CStr(DelimitedString)
Dim Substrings() As String
Substrings = Split(DelimitedString, StringDelimiter)
Dim sUpper As Long: sUpper = UBound(Substrings)
If sUpper < 1 Then Exit Function
Dim i As Long, j As Long, iPos As Long
Dim iSub As String, jSub As String
For i = 0 To sUpper - 1
iSub = Substrings(i)
For j = i + 1 To sUpper
jSub = Substrings(j)
'Debug.Print i, iSub, j, jSub
If Len(iSub) <= Len(jSub) Then
If InStr(1, jSub, iSub, vbTextCompare) > 0 Then
MatchPart = True
Exit Function
End If
Else
If InStr(1, iSub, jSub, vbTextCompare) > 0 Then
MatchPart = True
Exit Function
End If
End If
Next j
Next i
End Function

I want to check for similarity for text in corresponding cells in excel

I have a data set in my excel sheet, The data in each cell is a set of numbers separated by ";".
The expected result is in 2nd cell all the four numbers are there in both column (G and H), but not in same order. In the next row, the order is same. So is there any way to check the similarity
I have tried using below code but it only seem to highlight (red) first few characters
If anyone wants to see the file then click here
The code is :
Sub highlight()
Dim xRg1 As Range
Dim xRg2 As Range
Dim xTxt As String
Dim xCell1 As Range
Dim xCell2 As Range
Dim I As Long
Dim J As Integer
Dim xLen As Integer
Dim xDiffs As Boolean
On Error Resume Next
If ActiveWindow.RangeSelection.Count > 1 Then
xTxt = ActiveWindow.RangeSelection.AddressLocal
Else
xTxt = ActiveSheet.UsedRange.AddressLocal
End If
lOne:
Set xRg1 = Application.InputBox("Range A:", "Similarity finder", xTxt, , , , , 8)
If xRg1 Is Nothing Then Exit Sub
If xRg1.Columns.Count > 1 Or xRg1.Areas.Count > 1 Then
MsgBox "Multiple ranges or columns have been selected ", vbInformation, "Similarity finder"
GoTo lOne
End If
lTwo:
Set xRg2 = Application.InputBox("Range B:", "Similarity finder", "", , , , , 8)
If xRg2 Is Nothing Then Exit Sub
If xRg2.Columns.Count > 1 Or xRg2.Areas.Count > 1 Then
MsgBox "Multiple ranges or columns have been selected ", vbInformation, "Similarity finder"
GoTo lTwo
End If
If xRg1.CountLarge <> xRg2.CountLarge Then
MsgBox "Two selected ranges must have the same numbers of cells ", vbInformation, "Similarity finder"
GoTo lTwo
End If
xDiffs = (MsgBox("Click Yes to highlight similarities, click No to highlight differences ", vbYesNo + vbQuestion, "Similarity finder") = vbNo)
Application.ScreenUpdating = False
xRg2.Font.ColorIndex = xlAutomatic
For I = 1 To xRg1.Count
Set xCell1 = xRg1.Cells(I)
Set xCell2 = xRg2.Cells(I)
If xCell1.Value2 = xCell2.Value2 Then
If Not xDiffs Then xCell2.Font.Color = vbRed
Else
xLen = Len(xCell1.Value2)
For J = 1 To xLen
If Not xCell1.Characters(J, 1).Text = xCell2.Characters(J, 1).Text Then Exit For
Next J
If Not xDiffs Then
If J <= Len(xCell2.Value2) And J > 1 Then
xCell2.Characters(1, J - 1).Font.Color = vbRed
End If
Else
If J <= Len(xCell2.Value2) Then
xCell2.Characters(J, Len(xCell2.Value2) - J + 1).Font.Color = vbRed
End If
End If
End If
Next
Application.ScreenUpdating = True
End Sub
One way to do this is to use the first column to create a regular expression and apply it against the second column.
One advantage of using regex is that one of the data returned is the start and length of the match -- perfect for addressing the characters property of the range object.
I used early binding (see the reference to be set in the code notes), but you could use late binding if you must.
I also have the data in columns A & B, but you can alter that with the part of the code that defines the data location.
You should NOT need to use any On Error code. Much better to write the code to handle any forseeable errors. I did NOT do any error checking, and that may need to be added.
If speed is an issue, there are various other modifications which can be made.
The constructed regular expression will have the general appearance of
\b(?:nnn|nnn|nnn|nnn)\b
which means to
match a word boundary
match any of the pipe delimited substrings
match another word boundary.
For more information, see How to use Regular Expressions (Regex) in Microsoft Excel both in-cell and loops
Option Explicit
'Set reference to Microsoft VBScript Regular Expressions 5.5
Sub highLight()
Dim R As Range, C As Range, WS As Worksheet
Dim RE As RegExp, MC As MatchCollection, M As Match
Dim sSplit As String
'set the data range
' one column wide
' column 2 will be offset 1 to the left
'Obviously you can change this in many ways
'And also work in your user selected method as in your code.
'only requirement is that the ranges be single column, and you can
'easily check for that
Set WS = ThisWorkbook.Worksheets("Sheet4")
With WS
Set R = Range(.Cells(1, 1), .Cells(.Rows.Count, 1).End(xlUp))
End With
Set RE = New RegExp
Application.ScreenUpdating = False
With RE
.Global = True
'loop through the first column
For Each C In R.Rows
'replace the semicolon with the pipe
sSplit = Replace(Join(Split(C.Value, ";"), "|"), " ", "")
'since data has a terminal semi-colon, need to remove it if present
If Right(sSplit, 1) = "|" Then sSplit = Left(sSplit, Len(sSplit) - 1)
'finish construction of the regex pattern
.Pattern = "\b(?:" & sSplit & ")\b"
'check for matches and change relevant characters font color
Set MC = .Execute(C.Offset(columnoffset:=1))
With C.Offset(-0, 1)
.Font.Color = vbBlack
For Each M In MC
.Characters(M.FirstIndex + 1, M.Length).Font.Color = vbRed
Next M
End With
Next C
End With
End Sub
The heart of your question is how to test for your notion of similarity.
Here is a function which, when given a string consisting of semicolon-delimited items, returns True if the items are the same, though possibly in a different order, and False otherwise. The key idea is to split on ";", sort the resulting array, then rejoin. This will put the strings in a standard sorted order, which can then be directly compared for equality:
Function Sort(A As Variant) As Variant
Dim sorted As Variant, item As Variant
Dim lb As Long, ub As Long
Dim i As Long
Dim items As Object
Set items = CreateObject("System.Collections.ArrayList")
lb = LBound(A)
ub = UBound(A)
ReDim sorted(lb To ub)
For i = lb To ub
item = A(i) 'Somewhat oddly, seems needed
items.Add item
Next i
items.Sort
For i = lb To ub
sorted(i) = items(i - lb)
Next i
Sort = sorted
End Function
Function Similar(s1 As String, s2 As String) As Boolean
Similar = (Join(Sort(Split(s1, ";")), ";") = Join(Sort(Split(s2, ";")), ";"))
End Function
For example, Similar("12;34;56","34;56;12") will evaluate to True but Similar("12;34;56","34;16;12") will evaluate to False.
In your case you have trailing semicolons so they are not being used as delimiters in a standard way. It turns out not to matter: Similar("12;34;56;","34;56;12;") still works as expected.
Similarities
There is a problem with your data: the values in column A end with an "; ", while the values in column B end with ";". The solution presented is to use only the string before the last semi-colon ; with the Split function.
To avoid complications, I integrated the code into your rather cool solution.
The Code
Option Explicit
Sub highlightOrig()
Dim xRg1 As Range
Dim xRg2 As Range
Dim xTxt As String
Dim xCell1 As Range
Dim xCell2 As Range
Dim I As Long
Dim J As Integer
Dim xLen As Integer
Dim xDiffs As Boolean
On Error Resume Next
If ActiveWindow.RangeSelection.Count > 1 Then
xTxt = ActiveWindow.RangeSelection.AddressLocal
Else
xTxt = ActiveSheet.UsedRange.AddressLocal
End If
lOne:
Set xRg1 = Application.InputBox("Range A:", "Similarity finder", xTxt, , , , , 8)
If xRg1 Is Nothing Then Exit Sub
If xRg1.Columns.Count > 1 Or xRg1.Areas.Count > 1 Then
MsgBox "Multiple ranges or columns have been selected ", vbInformation, "Similarity finder"
GoTo lOne
End If
lTwo:
Set xRg2 = Application.InputBox("Range B:", "Similarity finder", "", , , , , 8)
If xRg2 Is Nothing Then Exit Sub
If xRg2.Columns.Count > 1 Or xRg2.Areas.Count > 1 Then
MsgBox "Multiple ranges or columns have been selected ", vbInformation, "Similarity finder"
GoTo lTwo
End If
If xRg1.CountLarge <> xRg2.CountLarge Then
MsgBox "Two selected ranges must have the same numbers of cells ", vbInformation, "Similarity finder"
GoTo lTwo
End If
xDiffs = (MsgBox("Click Yes to highlight similarities, click No to highlight differences ", vbYesNo + vbQuestion, "Similarity finder") = vbNo)
Application.ScreenUpdating = False
xRg1.Font.ColorIndex = xlAutomatic
xRg2.Font.ColorIndex = xlAutomatic
Const Delimiter As String = "; "
Dim dLen As Long: dLen = Len(Delimiter)
Dim s() As String
Dim d() As String
Dim cString As String
Dim n As Long
Dim cStart As Long
If Not xDiffs Then
For I = 1 To xRg1.Count
cStart = 1
Set xCell1 = xRg1.Cells(I)
Set xCell2 = xRg2.Cells(I)
cString = Left(xCell1.Value, InStrRev(xCell1.Value, ";") - 1)
s = Split(cString, Delimiter)
cString = Left(xCell2.Value, InStrRev(xCell2.Value, ";") - 1)
d = Split(cString, Delimiter)
For n = 0 To UBound(d)
If IsNumeric(Application.Match(d(n), s, 0)) Then
xCell2.Characters(cStart, Len(d(n))).Font.Color = vbRed
End If
cStart = cStart + Len(d(n)) + dLen
Next n
Next I
Else
For I = 1 To xRg1.Count
cStart = 1
Set xCell1 = xRg1.Cells(I)
Set xCell2 = xRg2.Cells(I)
cString = Left(xCell1.Value, InStrRev(xCell1.Value, ";") - 1)
s = Split(cString, Delimiter)
cString = Left(xCell2.Value, InStrRev(xCell2.Value, ";") - 1)
d = Split(cString, Delimiter)
For n = 0 To UBound(d)
If IsError(Application.Match(d(n), s, 0)) Then
xCell2.Characters(cStart, Len(d(n))).Font.Color = vbRed
End If
cStart = cStart + Len(d(n)) + dLen
Next n
Next I
End If
Application.ScreenUpdating = True
End Sub

I need an algorithm that count the number of words in column M that start with letters "a" and "A" in excel VBA

Here I have code that only counts the number of words and I dont know what to do to make it count the words that start with letter "A" and "a" in column M
Sub CountWords()
Dim xRg As Range
Dim xRgEach As Range
Dim xAddress As String
Dim xRgVal As String
Dim xRgNum As Long
Dim xNum As Long
On Error Resume Next
xAddress = ActiveWindow.RangeSelection.Address
Set xRg = Application.InputBox("Introduceti diapazonul:", "Selectare", xAddress, , , , , 8)
If xRg Is Nothing Then Exit Sub
Application.ScreenUpdating = False
If Application.WorksheetFunction.CountBlank(xRg) = xRg.Count Then
MsgBox "Numarul de cuvinte este: 0", vbInformation, ""
Exit Sub
End If
For Each xRgEach In xRg
xRgVal = xRgEach.Value
xRgVal = Application.WorksheetFunction.Trim(xRgVal)
If xRgEach.Value <> "" Then
xNum = Len(xRgVal) - Len(Replace(xRgVal, " ", "")) + 1
xRgNum = xRgNum + xNum
End If
Next xRgEach
MsgBox "Numarul de cuvinte: " & Format(xRgNum, "#,##0"), vbOKOnly, "Raspuns"
Application.ScreenUpdating = True
End Sub
Assuming that each cell contains a single word, use:
Sub ACount()
Dim i As Long, N As Long, Kount As Long
Dim ch As String
Kount = 0
N = Cells(Rows.Count, "M").End(xlUp).Row
For i = 1 To N
ch = Left(Cells(i, "M").Value, 1)
If ch = "a" Or ch = "A" Then Kount = Kount + 1
Next i
MsgBox Kount
End Sub
EDIT#1:
If the cells can contain more than one word (separated by spaces), the use:
Sub ACount()
Dim i As Long, N As Long, Kount As Long
Dim ch As String
Kount = 0
N = Cells(Rows.Count, "M").End(xlUp).Row
For i = 1 To N
arr = Split(Cells(i, "M").Value, " ")
For Each A In arr
ch = Left(A, 1)
If ch = "a" Or ch = "A" Then Kount = Kount + 1
Next A
Next i
MsgBox Kount
End Sub
Alternative via arrays including found word list display
It might be helpful to include a list of all valid words to the demanded count result.
Just to demonstrate a similar approach as Gary, but using arrays instead of a range loop,
I condensed the main procedure to three steps using a help function for step [1]:
[1] get data and provide for a sufficient wrds array by calling a help function getData()
[2] count & collect valid words in a loop through all words,
[3] display count cnt (or: UBound(wrds) plus list of valid words (►1-based 1-dim arraywrds)
Furthermore it's possible to analyze single words as well as word groups separated by spaces.
Sub ACount2()
Const SEARCHLETTER As String = "a" ' << change to any wanted search letter
'[1] get data and provide for sufficient wrds array
Dim allWrds, wrds: allWrds = getData(Sheet1, wrds) ' << change Sheet1 to your sheet's Code(Name)
'[2] count & collect valid words
Dim i As Long, letter As String, cnt As Long
For i = LBound(allWrds) To UBound(allWrds) ' loop through original words
letter = LCase(Left(allWrds(i), 1)) ' compare with search letter (lower case)
If letter = SEARCHLETTER Then cnt = cnt + 1: wrds(cnt) = allWrds(i)
Next i
ReDim Preserve wrds(1 To cnt)
'[3] display count plus list of valid words
MsgBox cnt & " words starting with {A|a}:" & _
vbNewLine & vbNewLine & _
Join(wrds, ", "), vbInformation
End Sub
Help function getData() called by above procedure
Function getData(sht As Worksheet, wrds, Optional ByVal col = "M", Optional ByVal StartRow As Long = 2)
'Purpose: get column data of a given worksheet and return to a "flat" array; provide for a sufficient wrds array
'a) get 2-dim data (starting in cell M2 by default) and transpose to 1-dim "flat" array
Dim lastRow As Long: lastRow = sht.Cells(sht.Rows.Count, col).End(xlUp).Row
Dim data: data = Split(Join(Application.Transpose(sht.Range(col & StartRow & ":" & col & lastRow)), " "), " ")
'b) provide for maximum elements in found words in calling procedure (implicit ByRef!)
ReDim wrds(1 To UBound(data))
'c) return 1-based "flat" 1-dim data array
getData = data
End Function

vba subroutine works on one sheet but not another

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

How to read e & é as the same thing using search macro in excel

I'm not entirely sure how to word this but, I have an Excel macro that enables a search functionality within my workbook. My issue is that I need the search to understand 'é' as 'e'. So that if I search for 'Belem', my result would come back with 'Belém'. How would I go about this? Thanks for any time and consideration.
Sub city()
If ActiveSheet.Name <> "City" Then Exit Sub
LastRow = ActiveSheet.Cells.SpecialCells(xlCellTypeLastCell).Row
Sheets("Results").Range("3:10000").Delete
SearchTerm = Application.InputBox("What are you looking for?")
Application.ScreenUpdating = False
Range("W1") = SearchTerm
Range("W2:W" & LastRow).FormulaR1C1 = _
"=IF(ISERR(SEARCH(R1C23,RC[-22]&RC[-21]&RC[-20]&RC[-19]&RC[-18]&RC[-17]&RC[-16]&RC[-15]&RC[-15]&RC[-14]&RC[-13]&RC[-12]&RC[-11]&RC[-10]&RC[-9]&RC[-8]&RC[-7]&RC[-6]&RC[-5]&RC[-4]&RC[-3]&RC[-2]&RC[-1])),0,1)"
If WorksheetFunction.CountIf(Columns(23), 1) = 0 Then
Columns(23).Delete
Application.ScreenUpdating = True
MsgBox "None found."
Else
For Each Cell In Range("A2:A" & LastRow)
If Cell.Offset(, 22) = 1 Then
Cell.Resize(, 51).Copy Sheets("Results").Range("A" & Rows.Count).End(xlUp).Offset(1)
x = x + 1
End If
Next Cell
Columns(22).Delete
Application.ScreenUpdating = True
If x = 1 Then
MsgBox "1 matching record was copied to Search Results tab."
Else
MsgBox x & " matching records were copied to Search Results tab."
End If
End If
End Sub
You can modify the search parameter and then use the like operator as follows:
Sub city()
Dim rngResult As Range
Dim searchTerm As String, counter As Integer
Dim values As Variant, value As Variant
If ActiveSheet.Name <> "City" Then Exit Sub
'First Cell with the results
Set rngResult = <First cell of the result Range>
'Uses a variant array to get all values from the range. This speeds up the routine
values = <Area of Search>.Value
'Converts to lowercase to do a case insensitive search (e.g. Belem = belem)
searchTerm = LCase(Application.InputBox("What are you looking for?"))
If searchTerm = "" Then Exit Sub
' "§" is just a placeholder
searchTerm = Replace(searchTerm, "e", "§")
searchTerm = Replace(searchTerm, "é", "§")
searchTerm = Replace(searchTerm, "§", "[eé]")
Application.ScreenUpdating = False
counter = 0
For Each value In values
If LCase(value) Like searchTerm Then
rngResult = value
Set rngResult = rngResult.Offset(1, 0) 'Moves to the next line
counter = counter + 1
End If
Next value
If counter = 0 Then
MsgBox "None found."
Else
MsgBox "Found " & counter & " results"
'Do what you need to do with the results
End If
Application.ScreenUpdating = True
End Sub
All the results will be at the column of rngResult.
The code works by replacing "e" and "é" by "§" and then replacing "§" by "[eé]", (e.g. "bélem" -> "bél§m" -> "b§l§m" -> "b[eé]l[eé]m").
The like will match either "e" or "é" on that position. You can learn more about it here or in the help files. Here is a Example:
bélem Like "b[eé]l[eé]m" ' true
belem like "b[eé]l[eé]m" ' true
recife like "b[eé]l[eé]m" ' false
You can search more graphs by adding other criteria like:
'Like will match "a","á", "à" and "ã"
searchTerm = Replace(searchTerm, "a", "§")
searchTerm = Replace(searchTerm, "á", "§")
searchTerm = Replace(searchTerm, "à", "§")
searchTerm = Replace(searchTerm, "ã", "§")
searchTerm = Replace(searchTerm, "§", "[aáàã]")
This method has the advantage that you only need one "translation" in order to do comparisons. This can improve the performance if you have a large dataset
You can keep an array of all the characters you want to replace and what you want to replace them with. It's easier if you "search" your data a little differently that using that formula. Here's how I would do it.
Sub FindCity()
Dim shResults As Worksheet
Dim vaData As Variant
Dim i As Long, j As Long
Dim sSearchTerm As String
Dim sData As String
Dim rNext As Range
'Put all the data into an array
vaData = ActiveSheet.UsedRange.Value
'Get the search therm
sSearchTerm = Application.InputBox("What are you looking for?")
'Define and clear the results sheet
Set shResults = ActiveWorkbook.Worksheets("Results")
shResults.Range("A3").Resize(shResults.UsedRange.Rows.Count, 1).EntireRow.Delete
'Loop through the data
For i = LBound(vaData, 1) To UBound(vaData, 1)
For j = LBound(vaData, 2) To UBound(vaData, 2)
'Get rid of diacritial characters
sData = LCase(Anglicize(vaData(i, j)))
'Look for a match
If InStr(1, sData, LCase(Anglicize(sSearchTerm))) > 0 Then
'Write the row to the next available row on Results
Set rNext = shResults.Cells(shResults.Rows.Count, 1).End(xlUp).Offset(1, 0)
rNext.Resize(1, UBound(vaData, 2)).Value = Application.Index(vaData, i, 0)
'Stop looking in that row after one match
Exit For
End If
Next j
Next i
End Sub
Public Function Anglicize(ByVal sInput As String) As String
Dim vaGood As Variant
Dim vaBad As Variant
Dim i As Long
Dim sReturn As String
'Replace any 'bad' characters with 'good' characters
vaGood = Split("S,Z,s,z,Y,A,A,A,A,A,A,C,E,E,E,E,I,I,I,I,D,N,O,O,O,O,O,U,U,U,U,Y,a,a,a,a,a,a,c,e,e,e,e,i,i,i,i,d,n,o,o,o,o,o,u,u,u,u,y,y", ",")
vaBad = Split("Š,Ž,š,ž,Ÿ,À,Á,Â,Ã,Ä,Å,Ç,È,É,Ê,Ë,Ì,Í,Î,Ï,Ð,Ñ,Ò,Ó,Ô,Õ,Ö,Ù,Ú,Û,Ü,Ý,à,á,â,ã,ä,å,ç,è,é,ê,ë,ì,í,î,ï,ð,ñ,ò,ó,ô,õ,ö,ù,ú,û,ü,ý,ÿ", ",")
sReturn = sInput
For i = LBound(vaBad) To UBound(vaBad)
sReturn = Replace$(sReturn, vaBad(i), vaGood(i))
Next i
Anglicize = sReturn
End Function
List of characters from Excel 2007 VBA Converting Accented Characters to Regular

Resources