Highlight duplicate value in a comma based string - excel

If I have values in cell c3= 2,4,6,8,12,14,18,23,35,78,101,38,30,205,2,101
In This two values are duplicate which is { 2,101 }
I want notification as and when enter any value twice, three-time , forth time, etc in that cell i should come to know which value is repeated. Duplicate values can be shown in adjacent cell D3,

Try this
Sub Test_CheckDups_UDF()
With Range("A1")
.Value = "2,4,6,8,12,14,18,23,35,78,101,38,30,205,2,101"
.Offset(, 1).Value = CheckDups(.Value)
End With
End Sub
Function CheckDups(s As String) As String
Dim a, dic As Object, i As Long
Set dic = CreateObject("scripting.dictionary")
a = Split(s, ",")
For i = LBound(a) To UBound(a)
If dic.Exists(a(i)) = True Then CheckDups = CheckDups & IIf(CheckDups = Empty, "", ",") & a(i) Else dic.Add a(i), 1
Next i
End Function
Here's a code that will highlight the duplicates within the same cell. Tweak it so as to suit your needs
Sub Highlight_Duplicates_Within_Cell()
Dim s, sp, k, c As Range, t As String, f As Boolean, n As Long
For Each c In Range("C3:C13")
c.Font.Color = vbBlack
With CreateObject("Scripting.Dictionary")
.CompareMode = vbTextCompare
sp = Split(c.Value, ",")
For Each s In sp
If Not .Exists(s) Then .Add s, 1 Else .Item(s) = .Item(s) + 1
Next s
For Each k In .Keys
t = "," & k & ","
f = False
n = InStr(1, "," & c.Value & ",", t, vbTextCompare)
Do While n And .Item(k) > 1
If f Then
c.Characters(n, Len(t) - 2).Font.Color = vbRed
End If
n = InStr(n + Len(k), "," & c.Value & ",", t, vbTextCompare)
f = True
Loop
Next k
End With
Next c
End Sub
Try this version too using Regex
Sub Highlight_Duplicates2()
Dim mtch As Object, mtch2 As Object, m As Object, mm As Object, c As Range, txt As String, i As Long
For Each c In Range("C3:C13")
With CreateObject("VBScript.RegExp")
.Global = True
txt = c.Value
.Pattern = " *(\w+)"
Set mtch = .Execute(txt)
For Each m In mtch
.Pattern = "\b" & m.submatches(0) & "\b"
Set mtch2 = .Execute(txt)
If mtch2.Count > 1 Then
For i = 1 To .Execute(txt).Count - 1
Set mm = mtch2(i)
With c.Characters(mm.firstindex + 1, mm.length).Font
.Color = vbRed: .Bold = True
End With
Mid$(txt, mm.firstindex + 1, mm.length) = Space(mm.length)
Next i
End If
Next m
End With
Next c
End Sub

This does not meet your request of trapping duplicates while typing. However to process a comma-separated string (once entered) consider the following user defined function:
Public Function duplist(s As String) As String
Dim s2 As String, arr
Dim kount As Long, i As Long, j As Long
arr = Split(s, ",")
For i = 0 To UBound(arr)
kount = 0
v = arr(i)
For j = 0 To i
If v = arr(j) Then kount = kount + 1
Next j
If kount = 2 Then s2 = s2 & "," & v
Next i
duplist = Mid(s2, 2)
End Function

Related

use VBA to color specific words in a list

SO I have a list of words ( they are 250ish medications in my Settings sheet ) , and I want to use vba to find those specific words in Column D of another sheet and color them magenta. Column D has 105 cells that are full of text.
text I want to search:
list of meds:
what I want it to look like:
below is what iv gathered from other resources but I just cant get it to work! please let me know if you have any suggestions!
also it kinda has to work with mac and windows excel
Sub ColorWords3()
Dim Position As Long, Cell As Range, W As Variant, Words As Variant, Txt As String, druglastcol As Variant, drugs As Variant
druglastcol = Sheets("Settings").Range("A" & Rows.Count).End(xlUp).Row
'Words = Array("TEXT", "WORD", "THEN")
Words = Application.Transpose(Sheets("Settings").Range("A4:A" & druglastcol).Text)
For Each Cell In Columns("D").SpecialCells(xlConstants)
Txt = " " & UCase(Cell.Value) & " "
For Each W In Words
Position = InStr(Txt, W)
Do While Position > 0
If Mid(Txt, Position - 1, Len(W) + 2) Like "[!A-Z0-9]" & W & "[!A-Z0-9]" Then
With Cell.Characters(Position - 1, Len(W)).Font
.Bold = True
.Color = vbRed
End With
End If
Position = InStr(Position + 1, Txt, W)
Loop
Next
Next
End Sub
Like is case-sensitive, so you need to upper-case your drug names to match your upper-cased blocks of text.
If Mid(Txt, Position - 1, Len(W) + 2) Like "[!A-Z0-9]" & UCase(W) & "[!A-Z0-9]" Then
Using Like gets a bit clunky so here's a RegExp-based approach:
EDIT - added a working Like/InStr version...
Sub ColorWords()
Dim Cell As Range, W, Words, matches As Collection, m
With Sheets("Settings")
Words = Application.Transpose(.Range(.Range("A4"), _
.Cells(.Rows.Count, 1).End(xlUp)))
End With
For Each Cell In ActiveSheet.Columns("D").SpecialCells(xlConstants)
For Each W In Words
'Set matches = AllMatchesRegEx(Cell.Text, W) 'windows only
Set matches = AllMatchesInStr(Cell.Text, W) 'windows+mac
For Each m In matches
Debug.Print Cell.Address, W, m
With Cell.Characters(m, Len(W)).Font
.Bold = True
.Color = vbMagenta
End With
Next m
Next
Next
End Sub
Function AllMatchesInStr(ByVal textToSearch As String, searchTerm)
Const OUT As String = "[!A-Z0-9]"
Dim rv As New Collection, pos As Long, start As Long
Dim next2 As String, next1 As String
textToSearch = UCase(" " & textToSearch & " ")
start = 1
pos = InStr(start, textToSearch, searchTerm, vbTextCompare)
Do While pos > 0
If Mid(textToSearch, pos - 1, 1) Like OUT Then
next2 = Mid(textToSearch, pos + Len(searchTerm), 2)
next1 = Left(next2, 1)
'Handle possible s at end of search term
If next1 Like OUT Or (next2 Like "S" & OUT) Then
rv.Add pos - 1
End If
End If
start = pos + 1
pos = InStr(start, textToSearch, searchTerm, vbTextCompare)
Loop
Set AllMatchesInStr = rv
End Function
Function AllMatchesRegEx(textToSearch As String, searchTerm)
Dim rv As New Collection, matches, m
Static reg As Object
If reg Is Nothing Then
Set reg = CreateObject("VBScript.RegExp")
reg.Global = True
reg.IgnoreCase = True
End If
reg.Pattern = "\b" & searchTerm & "s?\b" 'Allow for simple plural form,
'flank with word boundaries
Set matches = reg.Execute(textToSearch)
For Each m In matches
rv.Add m.firstindex + 1 'firstindex is zero-based
Next m
Set AllMatchesRegEx = rv
End Function
There is a mistake in your code:
Words = Application.Transpose(Sheets("Settings").Range("A4:A" & Dr).Text)
what is Dr?
Also don't do this:
druglastcol = Sheets("Settings").Range("A4:A" & Rows.Count).End(xlDown).Row
Do this instead:
druglastcol = Sheets("Settings").Range("A" & Rows.Count).End(xlUp).Row
The reason we do it this way is the method you have used will stop if there is a blank row in the data, the method i have posted comes from the bottom up so will always grab the true last row.
Try
Sub test()
Dim Ws As Worksheet
Dim s As String
Dim vDB
Dim i As Long
'Application.ScreenUpdating = False
Set Ws = Sheets("Settings")
With Ws
vDB = .Range("a1", .Range("a" & Rows.Count).End(xlUp))
End With
For i = 1 To UBound(vDB, 1)
s = vDB(i, 1)
setCharacterColor s
Next i
'Application.ScreenUpdating = True
End Sub
Sub setCharacterColor(strPattern As String)
Dim mCol As Object 'MatchCollection
Dim Ws As Worksheet
Dim rngDB As Range, rng As Range
Dim s As String
Dim i As Integer, Ln As Integer
Set Ws = Sheets("Facts")
Set rngDB = Ws.Range("d1", Ws.Range("d" & Rows.Count).End(xlUp))
For Each rng In rngDB
s = rng.Value
Set mCol = GetRegEx(s, strPattern)
If Not mCol Is Nothing Then
For i = 0 To mCol.Count - 1
c = mCol.Item(i).FirstIndex + 1
Ln = mCol.Item(i).Length
With rng.Characters(c, Ln).Font
.Bold = True
.Color = vbMagenta
End With
Next i
End If
Next
End Sub
Function GetRegEx(StrInput As String, strPattern As String) As Object
Dim RegEx As Object 'New RegExp
Set RegEx = CreateObject("VBScript.RegExp") 'New RegExp
With RegEx
.Global = True
.IgnoreCase = False
.MultiLine = True
.Pattern = strPattern
End With
If RegEx.test(StrInput) Then
Set GetRegEx = RegEx.Execute(StrInput)
End If
End Function
If your use Mac then try below.
Sub test()
Dim Ws As Worksheet, WsColor As Worksheet
Dim rngDB As Range, rng As Range
Dim s As String
Dim vDB, vR
Dim i As Long, Ln As Integer
Dim j As Index
Dim st, et
Application.ScreenUpdating = False
st = Timer
Set Ws = Sheets("Settings")
Set WsColor = Sheets("Facts")
With Ws
vDB = .Range("a1", .Range("a" & Rows.Count).End(xlUp))
End With
With WsColor
Set rngDB = .Range("d1", .Range("d" & Rows.Count).End(xlUp))
End With
For Each rng In rngDB
For i = 1 To UBound(vDB, 1)
Ln = Len(vDB(i, 1)) 'String Length
vR = getItem(rng, vDB(i, 1)) 'string startedIndex
If IsArray(vR) And Not IsEmpty(vR) Then
For j = 1 To UBound(vR)
With rng.Characters(vR(j), Ln).Font
.Bold = True
.Color = vbMagenta
End With
Next j
End If
Next i
Next rng
Application.ScreenUpdating = True
et = Timer
Debug.Print et - st
End Sub
Function getItem(rng As Range, v As Variant) As Variant
Dim vR()
Dim k As Integer, s As Integer, n As Index
Dim str As String
str = rng.Text
s = 1
Do
n = InStr(s, str, v)
If n > 0 Then
k = k + 1
ReDim Preserve vR(1 To k)
vR(k) = n
End If
s = n + Len(v)
DoEvents
Loop While n > 0
If k Then
getItem = vR
Else
getItem = Empty
End If
End Function

How can I add looping per 250 cells and offset the array?

I have this code that looks at column A and loops through to create an array to paste to another destination, but I want to manipulate it to loop through sets of 250 cells and create a concatenated array and print it to cells B1. After that set of 250, I go cells a251-a501, and so forth until I reach the end of the list and have each set of 250 concatenated ID's (separated by a ";") to print to the next destination row (B1>B2>B3, etc..)
Sub JC_Fill()
Dim varArray() As Variant
Dim x As Long, i As Long
i = 0
x = 1
With ThisWorkbook.Worksheets("Sheet1").UsedRange.Rows.Count
ReDim varArray(1) 'resize array
Do Until Cells(x, 1).Value = ""
If Cells(x, 1) <> "" Then
varArray(i) = Cells(x, 1).Value
i = i + 1
ReDim Preserve varArray(i)
End If
x = x + 1
Loop
ReDim Preserve varArray(i - 1)
End With
ThisWorkbook.Worksheets("Sheet1").Range("B1").Value = varArray
End Sub
How could I edit my Do While/Loop to repeat the process every 250 cells and then concatenate the array to one cell separated by ; and then offset the next batch until I have no more ID's to cycle through?
Try changing your code this way:
Sub JC_Fill()
Dim OutString
Dim x As Long, i As Long
Dim out_row As Long
i = 0
x = 1
out_row = 1
With ThisWorkbook.Worksheets("Sheet1").UsedRange.Rows.Count
OutString = ""
Do Until Cells(x, 1).Value = ""
If Cells(x, 1) <> "" Then
If (x > 1) Then OutString = OutString & ";"
OutString = OutString & Cells(x, 1).Value
End If
If (x Mod 250) = 0 Then
ThisWorkbook.Worksheets("Sheet1").Range("B" & out_row).Value = OutString
OutString = ""
out_row = out_row + 1
End If
x = x + 1
Loop
End With
ThisWorkbook.Worksheets("Sheet1").Range("B" & out_row).Value = OutString
End Sub
For interest, you can do this without looping each of the 250 cells.
Sub x()
Dim n As Long, v As Variant, r As Range, n2 As Long
n = 5 '250 for you
n2 = Range("A" & Rows.Count).End(xlUp).Row
Set r = Range("A1").Resize(n)
Do While Len(r(1)) > 0
If n2 - r(1).Row < n Then Set r = r.Resize(n2 - r(1).Row + 1)
If r.Count = 1 Then
v = r.Value
Else
v = Join(Application.Transpose(r), ";")
End If
Range("B" & Rows.Count).End(xlUp)(2).Value = v
Set r = r.Offset(n)
Loop
End Sub

Testing variants against each other

The goal is to get unused values in the textbox, currently i get all of them, se below
This is what I´m trying to get..
..and finally(don't know how to formulate the question yet) this..
My code so far..
It fails to recognize any matches on line 21 (If x = y Then match = True)
Option Explicit
Sub Resources()
Application.ScreenUpdating = False
Dim Arr As Variant
Arr = Range("A2:A10").Value
Dim varr As Variant
varr = Application.Transpose(ExtractNumbers(Range("C2:E10")))
ActiveSheet.TextBox1.Text = "Unused values"
Dim i As Integer
i = 1
Dim x As Variant, y As Variant, z As Variant
Dim match As Boolean
For Each x In Arr
match = False
For Each y In varr
If x = y Then match = True
Next y
If Not match And x > 0 Then
ActiveSheet.TextBox1.Text = ActiveSheet.TextBox1.Text & Chr(10) & x
End If
i = i + 1
Next
Application.ScreenUpdating = True
End Sub
Public Function ExtractNumbers(Target As Range) As Variant
Dim regEx As Object
Set regEx = CreateObject("vbscript.regexp")
Dim regExMatches As Object, regExMatch As Object
Dim Result As String
Dim Cell As Range
For Each Cell In Target
If Cell.Value <> vbNullString Then
With regEx
.Global = True
.MultiLine = True
.IgnoreCase = False
.Pattern = "[0-9]+"
End With
Set regExMatches = regEx.Execute(Cell.Value)
For Each regExMatch In regExMatches
Result = Result & regExMatch & ", "
Next regExMatch
End If
Next Cell
ExtractNumbers = Split(Left$(Result, Len(Result) - 1), ", ")
End Function
Collect the values into a vbLF delimited list before depositing them onto the worksheet.
Option Explicit
Sub resources()
Dim i As Long, str As String
With Worksheets("sheet6")
'collect the missing
For i = 2 To .Cells(.Rows.Count, "A").End(xlUp).Row
If Not CBool(Application.CountIf(.Range("C:E"), .Cells(i, "A").Value)) Then
str = Chr(10) & .Cells(i, "A").Value & Space(1) & .Cells(i, "B").Value & str
End If
Next i
'put results in merged cell
If CBool(Len(str)) Then
str = "unused values" & str
.Range("F:F").UnMerge
.Cells(1, "F").Resize(UBound(Split(str, Chr(10))) + 1, 1).Merge
.Cells(1, "F").WrapText = True
.Cells(1, "F") = str
End If
End With
End Sub

vba removing duplicates from new set

The code below creates a new set of data on a new sheet but it takes everything that matches the requirements even if it is a duplicate. How can I change the code to eliminate duplicates in the new data set?
Sub Testerss()
Dim c As Range, v As String, arr, x As Long, e
Dim d As Range
Dim ws As Worksheet
Set d = Worksheets("Sheet3").Range("D1")
For Each c In ActiveSheet.Range("D25:D105")
v = Trim(c.Value)
If Len(v) > 0 Then
v = Replace(v, vbLf, " ")
Do While InStr(v, " ") > 0
v = Replace(v, " ", " ")
Loop
arr = Split(v, " ")
For x = LBound(arr) To UBound(arr)
e = arr(x)
If Not IsError(Application.Match(LCase(e), Array("(bye)", "(hello)"), 0)) Then
If x > LBound(arr) Then
d.Value = arr(x - 1) & " " & e
Else
d.Value = "??? " & e
End If
Set d = d.Offset(1, 0)
End If
Next x
End If
Next c
End Sub​
You could add a check to see if the result has already been copied over. First set a range of the results
finalRow = Worksheets("Sheet3").Cells(1000000, 4).end(xlup).row
Set resultRange = Worksheets("Sheet3").Range("D1:D" & finalRow)
Now see if the value you are currently examining is in that range
duplicate = false
for each result in resultRange
if v = result.Value then
duplicate = true
Exit For
end if
next
Now also check for duplicate before proceeding
If Len(v) > 0 and not duplicate then
All together
Set d = Worksheets("Sheet3").Range("D1")
For Each c In ActiveSheet.Range("D25:D105")
finalRow = Worksheets("Sheet3").Cells(1000000, 4).end(xlup).row
Set resultRange = Worksheets("Sheet3").Range("D1:D" & finalRow)
v = Trim(c.Value)
duplicate = false
for each result in resultRange
if v = result.Value then
duplicate = true
Exit For
end if
next
If Len(v) > 0 and not duplicate then
...

String operation VBA Excel

I am struggling with the following problem.
I want to do following operations on Input Col A and produce output in col B:
1.Remove Duplicates if any ( It was easy and completed )
2.Remove Leading and/or Trailing spaces from the string (It was easy as well and it's done )
3.COLLECT THE DIFFERENT TRANSLATIONS OF A WORD IN SAME CELL - AVOID DUPLICATES ( It's hard and I don't know how to proceed with this problem )
To understand this point have a look at input/output example.
Input:
A
 absolution
 absolution
 absolutism
 absolutism, absolute rule
  absolutist   
  absolutist   
 absorb
 absorb
 absorb, bind
 absorb, take up
 absorb
 absorb, imbibe, take up
 absorb, sorb
 absorb, take up
 absorb, take up
 absorb, imbibe
 absorb
 absorb
 absorber
 absorber
 absorber
Output:
col B
absolution
absolutism, absolute rule
absolutist
absorb, bind, imbibe, take up, sorb
absorber
I tried with the following code but I am stuck on the third point/step
Option Explicit
Sub StrMac()
Dim wk As Worksheet
Dim i, j, l, m As Long
Dim strc, strd, fstrc, fstrd As String
Dim FinalRowC, FinalRowD As Long
Set wk = Sheet1
wk.Columns(1).Copy Destination:=wk.Columns(3)
wk.Columns(2).Copy Destination:=wk.Columns(4)
wk.Range("$C:$C").RemoveDuplicates Columns:=1, Header:=xlNo
wk.Range("$D:$D").RemoveDuplicates Columns:=1, Header:=xlNo
FinalRowC = wk.Range("C1048576").End(xlUp).Row
FinalRowD = wk.Range("D1048576").End(xlUp).Row
If FinalRowC >= FinalRowD Then
j = FinalRowC
Else
j = FinalRowD
End If
For i = 1 To j
If wk.Range("C" & i).Text <> "" Then
strc = wk.Range("C" & i).Text
strc = Replace(strc, Chr(160), "")
strc = Application.WorksheetFunction.Trim(strc)
wk.Range("C" & i).Value = strc
Else: End If
If wk.Range("D" & i).Text <> "" Then
strd = wk.Range("D" & i).Text
strd = Replace(strd, Chr(160), "")
strd = Application.WorksheetFunction.Trim(strd)
wk.Range("D" & i).Value = strd
Else: End If
Next i
Dim Cet, Det, Fet, Met, s As Variant
Dim newstr
Dim pos, cos As Long
s = 1
For i = 1 To j
If wk.Range("D" & i).Text <> "" Then
l = 2
strd = wk.Range("D" & i).Text
newstr = strd
For m = i + 1 To j
pos = 1100
cos = 2300
fstrd = wk.Range("D" & m).Text
cos = InStr(1, fstrd, ",")
pos = InStr(1, fstrd, strd, vbTextCompare)
If wk.Range("D" & m).Text <> "" And Len(fstrd) > Len(strd) And m <= j And cos <> 2300 And pos = 1 Then
l = 5
newstr = newstr & "," & fstrd
wk.Range("D" & m) = ""
Else: End If
Next m
wk.Range("E" & s) = newstr
s = s + 1
Else: End If
Next i
End Sub
Assuming your input is column A and you want the output in column B (as stated in your question), the following should work for you:
Sub tgr()
Dim ws As Worksheet
Dim rData As Range
Dim aData As Variant
Dim vData As Variant
Dim vWord As Variant
Dim aResults() As String
Dim sUnq As String
Dim i As Long
Set ws = ActiveWorkbook.Sheets("Sheet1")
Set rData = ws.Range("A1", ws.Cells(Rows.Count, "A").End(xlUp))
If rData.Cells.Count = 1 Then
'Only 1 cell in the range, check if it's no blank and output it's text
If Len(Trim(rData.Text)) > 0 Then ws.Range("B1").Value = WorksheetFunction.Trim(rData.Text)
Else
'Remove any extra spaces and sort the data
With rData
.Value = Evaluate("index(trim(" & .Address(external:=True) & "),)")
.Sort .Cells, xlAscending, Header:=xlNo
End With
aData = rData.Value 'Load all values in range to array
ReDim aResults(1 To rData.Cells.Count, 1 To 1) 'Ready the results array
For Each vData In aData
'Get only unique words
If InStr(1, vData, ",", vbTextCompare) = 0 Then
If InStr(1, "," & sUnq & ",", "," & vData, vbTextCompare) = 0 Then
sUnq = sUnq & "," & vData
If i > 0 Then aResults(i, 1) = Replace(aResults(i, 1), ",", ", ")
i = i + 1
aResults(i, 1) = vData
End If
Else
'Add unique different translations for the word
For Each vWord In Split(vData, ",")
If InStr(1, "," & aResults(i, 1) & ",", "," & Trim(vWord) & ",", vbTextCompare) = 0 Then
aResults(i, 1) = aResults(i, 1) & "," & Trim(vWord)
End If
Next vWord
End If
Next vData
End If
'Output results
If i > 0 Then ws.Range("B1").Resize(i).Value = aResults
End Sub

Resources