Remove Duplicates and make unique list - excel

The Fruits contains list - Apple,Banana,Orange
and
Colors contains list - Red,Black,Orange
so when I multi select the Fruits as well as Colors from drop-down list from cell "G1". Then the "Offset(0, -1)" means "F1" shows me the combine output list as - (Apple, Banana, Orange, Red, Black, Orange).
So, The list in cell "F1" contains duplicate value Orange and it prints 2 times.
It should pick up only unique items from the selected one and remove the duplicate one and should print in cell F1 as - (Apple, Banana, Orange, Red, Black).
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
Dim rngDV As Range, oldVal As String, newVal As String
Dim arr As Variant, El As Variant
If Target.count > 1 Then GoTo exitHandler
If Target.value = "" Then
Application.EnableEvents = False
Target.Offset(0, -1).value = ""
Application.EnableEvents = True
Exit Sub
End If
On Error Resume Next
Set rngDV = cells.SpecialCells(xlCellTypeAllValidation)
On Error GoTo exitHandler
If rngDV Is Nothing Then GoTo exitHandler
If Not Intersect(Target, rngDV) Is Nothing Then
Application.EnableEvents = False
newVal = Target.value: Application.Undo
oldVal = Target.value: Target.value = newVal
If Target.Column = 7 Then
If oldVal <> "" Then
If newVal <> "" Then
arr = Split(oldVal, ",")
For Each El In arr
If El = newVal Then
Target.value = oldVal
GoTo exitHandler
End If
Next
Target.value = oldVal & "," & newVal
Target.EntireColumn.AutoFit
End If
End If
End If
writeSeparatedStringLast Target
End If
exitHandler:
Application.EnableEvents = True
End Sub
Sub writeSeparatedStringLast(rng As Range)
Dim arr As Variant, arrFin As Variant, El As Variant, k As Long, listBox As MSForms.listBox
Dim arrFr As Variant, arrVeg As Variant, arrAnim As Variant, El1 As Variant
Dim strFin As String ', rng2 as range
arrFr = Split("Apple,Banana,Orange", ",")
arrVeg = Split("Onion,Tomato,Cucumber", ",")
arrAnim = Split("Red,Black,Orange", ",")
arr = Split(rng.value, ",")
For Each El In arr
Select Case El
Case "Fruits"
arrFin = arrFr
Case "Vegetables"
arrFin = arrVeg
Case "Colors"
arrFin = arrAnim
End Select
For Each El1 In arrFin
strFin = strFin & El1 & ", "
Next
Next
strFin = left(strFin, Len(strFin) - 1)
With rng.Offset(0, -1)
.value = strFin
.WrapText = True
.Select
End With
End Sub
'Firstly run the next Sub, in order to create a list validation in range "G1":
Sub CreateValidationBis()
Dim sh As Worksheet, rng As Range
Set sh = ActiveSheet
Set rng = sh.Range("G1")
With rng.Validation
.Delete
.Add Type:=xlValidateList, AlertStyle:=xlValidAlertStop, _
Operator:=xlBetween, Formula1:="Fruits,Vegetables,Colors"
.IgnoreBlank = True
.InCellDropdown = True
.ShowInput = True
.ShowError = True
End With
End Sub

Is this code will fit to remove the duplicates from output arrays and give me the unique value.
Public Function RemoveDuplicateWords(InputString As String) As String
Dim InputArray() As String
InputArray = Split(InputString, " ")
Dim DictUnique As Object
Set DictUnique = CreateObject("Scripting.Dictionary")
Dim OutputString As String
Dim Word As Variant
For Each Word In InputArray
If Not DictUnique.Exists(Word) Then
DictUnique.Add Word, 1
OutputString = OutputString & " " & Word
End If
Next Word
RemoveDuplicateWords = Trim$(OutputString)
End Function

Related

Find any part of a text string of keywords separated by spaces

I'm trying to match any part of a text string of keywords to text in the Target cell.
If it is a text value, of a single keyword, like "wal" for Wal-mart, then this code finds the text string in the Target, i.e. " WAL-MART SUPERCENTER ".
When I have "wal otherstore" in the keywords cell it doesn't find Wal-mart anymore. I thought that is what xlPart was supposed to do.
Set Found = Target.Find(What:=Cell, LookIn:=xlValues, LookAt:=xlPart, MatchCase:=False)
The idea is to have a list of accounts in a budget spreadsheet with associated keywords, so that the macro then finds keywords, like "wal" and assigns the Target entry to that account.
The keywords are all in a single cell next to the accounts list. The keywords are separated by a space.
Full code:
Private Sub Worksheet_Change(ByVal Target As Range)
Application.ScreenUpdating = False
Dim Rng1 As Range, Rng2 As Range, Rng3 As Range, Cell As Range, Found As Range
Dim List As String
Dim Qty As Integer
Dim Coll As Collection
Dim i As Long
LastRowA = Sheets("Transactions").Cells(Rows.Count, "A").End(xlUp).Row
LastRowL = Sheets("Transactions").Cells(Rows.Count, "L").End(xlUp).Row
List = "Reset Options"
Qty = 0
Set Coll = New Collection
Set Rng1 = Sheets("Transactions").Range("G3:G" & LastRowA)
Set Rng2 = Sheets("Accounts & Budget").Range("I5:I104")
Set Rng3 = Sheets("Transactions").Range("L3:L" & LastRowL)
If Application.Intersect(Target, Rng1) Is Nothing Then
ElseIf Application.Intersect(Target, Rng1).Address = Target.Address Then
If Target = "" Then
Application.EnableEvents = False
Target.Offset(0, 5).Validation.Delete
Cells(Target.Row, "G") = ""
Cells(Target.Row, "L") = ""
Application.EnableEvents = True
ElseIf Target <> "" Then
For Each Cell In Rng2
Set Found = Target.Find(What:=Cell, LookIn:=xlValues, LookAt:=xlPart, MatchCase:=False)
If Found Is Nothing Then
Else
Qty = Qty + 1
Coll.Add Cell.Offset(0, -2)
End If
Next Cell
If Qty = 1 And Coll.Count = 1 Then
Application.EnableEvents = False
Target.Offset(0, 5).Validation.Delete
Target.Offset(0, 5) = Coll(1)
Target.Offset(0, 5).Validation.Add Type:=xlValidateList, Operator:=xlBetween, Formula1:="=Accounts"
Application.EnableEvents = True
ElseIf Qty = 0 And Coll.Count = 0 Then
Application.EnableEvents = False
Target.Offset(0, 5).Validation.Delete
Target.Offset(0, 5) = "No Results - Select From List"
Target.Offset(0, 5).Validation.Add Type:=xlValidateList, Operator:=xlBetween, Formula1:="=Accounts"
Application.EnableEvents = True
ElseIf Qty > 1 And Coll.Count > 1 Then
Application.EnableEvents = False
Target.Offset(0, 5).Validation.Delete
Target.Offset(0, 5) = "Multiple Results - Select From List"
For i = 1 To Coll.Count
List = List & "," & Coll(i)
Next i
Target.Offset(0, 5).Validation.Add Type:=xlValidateList, Operator:=xlBetween, Formula1:=List
Application.EnableEvents = True
End If
End If
End If
If Application.Intersect(Target, Rng3) Is Nothing Then
ElseIf Application.Intersect(Target, Rng3).Address = Target.Address Then
If Target = "Reset Options" Or Target = "" Then
Application.EnableEvents = False
Target.Validation.Delete
Target.Validation.Add Type:=xlValidateList, Operator:=xlBetween, Formula1:="=Accounts"
Application.EnableEvents = True
Target = "Select From List"
ElseIf Target.Offset(0, -5) = "" Then
Application.EnableEvents = False
Target.Validation.Delete
Target = ""
Application.EnableEvents = True
End If
End If
Application.ScreenUpdating = True
End Sub

Log changes (for specific column and giveback of a specific column)

I am hardly familiar with vba but now need an excel whose changes should be logged. I have now found the following code on stack, but still need two adjustments that I can not manage myself. I only need the monitoring of the column K (K2:K2000), if it changes something there that only that is logged. And if I always need the content of column A, for example if she changes something in column K33 then I want the value A33 as the seventh display in my log.
I tried to understand the code, but I couldn't do it myself.I found the following code on stack overflow:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim RangeValues As Variant, r As Long, boolOne As Boolean, TgValue 'the array to keep Target values (before UnDo)
Dim sh As Worksheet: Set sh = Worksheets("Protokoll")
Dim UN As String: UN = Application.userName
'sh.Unprotect "" 'it should be good to protect the sheet
If sh.Range("A1") = "" Then sh.Range("A1").Resize(1, 6) = _
Array("Time", "User Name", "Changed cell", "From", "To", "Sheet Name")
Application.ScreenUpdating = False 'to optimize the code (make it faster)
Application.Calculation = xlCalculationManual
If Target.cells.count > 1 Then
TgValue = extractData(Target)
Else
TgValue = Array(Array(Target.value, Target.Address(0, 0))) 'put the target range in an array (or as a string for a single cell)
boolOne = True
End If
Application.EnableEvents = False 'avoiding to trigger the change event after UnDo
Application.Undo
RangeValues = extractData(Target) 'define the RangeValue
putDataBack TgValue, ActiveSheet 'put back the changed data
If boolOne Then Target.Offset(1).Select
Application.EnableEvents = True
Dim columnHeader As String, rowHeader As String
For r = 0 To UBound(RangeValues)
If RangeValues(r)(0) <> TgValue(r)(0) Then
sh.cells(rows.count, 1).End(xlUp).Offset(1, 0).Resize(1, 6).value = _
Array(Now, UN, RangeValues(r)(1), RangeValues(r)(0), TgValue(r)(0), Target.Parent.Name)
End If
Next r
'sh.Protect ""
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
End Sub
Sub putDataBack(arr, sh As Worksheet)
Dim i As Long, arrInt, El
For Each El In arr
sh.Range(El(1)).value = El(0)
Next
End Sub
Function extractData(rng As Range) As Variant
Dim a As Range, arr, count As Long, i As Long
ReDim arr(rng.cells.count - 1)
For Each a In rng.Areas 'creating a jagged array containing the values and the cells address
For i = 1 To a.cells.count
arr(count) = Array(a.cells(i).value, a.cells(i).Address(0, 0)): count = count + 1
Next
Next
extractData = arr
End Function

Worksheet change Event working like after update event of combobox

Ok i want to make a searchable drop down list data validation without using helper column or combobox control...So how can i accomplish this..Everything is working fine but let say if i put at then click drop down arrow it will not calculate the worksheet change event..I want to run the worksheet change event every time value change in a certain cell..Suppose if i type at then worksheet change event should run 2 times..I mean every time i click in a keyboard alphabet then the worksheet change event need to run.How can i accomplish this...
Here is my code:
Worksheet Change Event:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim DestinationRng As Range, SourceRng As Range
Set SourceRng = Range("A1:A25")
Set DestinationRng = Range("C1:C25")
If Not Application.Intersect(DestinationRng, Range(Target.Address)) Is Nothing Then
'Target.Validation.Delete
If Target.Value = "" Then
DVDL SourceRng, Target, ""
Else
DVDL SourceRng, Target, Range(Target.Address).Value
End If
End If
End Sub
Here is worksheet selection change event
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Dim DestinationRng As Range, SourceRng As Range
Set SourceRng = Range("A1:A25")
Set DestinationRng = Range("C1:C25")
' 'If Target = ActiveCell Then Debug.Print yes
Debug.Print "Active:" & ActiveCell.Address
Debug.Print Target.Address
If Not Application.Intersect(DestinationRng, Range(Target.Address)) Is Nothing Then
If Target.Value = "" Then
DVDL SourceRng, Target, ""
Else
DVDL SourceRng, Target, Range(Target.Address).Value
End If
End If
End Sub
Here is the data validation sub procedure:
Public Sub DVDL(SourceRng As Range, PlaceRng As Range, SearchTxt As String)
Dim arr As Variant, arr2 As Variant
If SourceRng.Columns.Count > 1 Then
arr = Application.WorksheetFunction.Transpose(Application.WorksheetFunction.Transpose(SourceRng.Value))
ElseIf SourceRng.Rows.Count > 1 Then
arr = Application.WorksheetFunction.Transpose(SourceRng.Value)
End If
arr = RemoveDuplicateS(arr)
If SearchTxt = "" Then
arr2 = arr
Else
arr2 = Filter(arr, SearchTxt, , vbTextCompare)
End If
For Each el In arr2
Debug.Print el
Next el
If LBound(arr2) <> UBound(arr) Then
'PlaceRng.Select
With PlaceRng.Validation
.Delete
.Add Type:=xlValidateList, AlertStyle:=xlValidAlertStop, Operator:= _
xlBetween, Formula1:=Join(arr2, ",")
.IgnoreBlank = True
.InCellDropdown = True
.InputTitle = ""
.ErrorTitle = ""
.InputMessage = ""
.ErrorMessage = ""
.ShowInput = True
.ShowError = False
End With
End If
End Sub
Here is the code for Removing duplicate value from source range:Not required if source contain unique value
Public Function RemoveDuplicateS(arr As Variant) As Variant
'From this function we return an array from an sorted array.
'It doesn't required extra space in memory because we use same array.
Dim i As Long, j As Long
If IsEmpty(arr) Then
RemoveDuplicateS = arr(0) 'If incoming array is empty then return empty one.
Else
j = LBound(arr)
For i = LBound(arr) To UBound(arr) - 1 'Run loop from first one to second last one.
If arr(i) <> arr(i + 1) Then 'if arr(5)<>arr(6) then put put the arr(5) value to the unique list.
arr(j) = arr(i)
j = j + 1 'Increase the j for indexing.
End If
Next i
arr(j) = arr(UBound(arr)) 'Put the last data to unique list.
ReDim Preserve arr(LBound(arr) To j) 'Delete the extra data from the array.
RemoveDuplicateS = arr 'Return the array.
End If
End Function

Use VBA to select Excel cell values conditionally based on cell format (cell background color)

i want to print value where background yellow is same as cell and background white is [0,0]
for this condition i want to get
[1,9],[0,0],[1,7],[1,6],[0,0],[1,4],[0,0],[1,2],[0,0]
I've written some code
Dim isect As Range
Set isect = Intersect(Target, Me.Range("$B$80:$J$80"))
If Not isect Is Nothing Then
Dim s As String
If Target.Interior.Color = vbYellow Then
s = Target.Value
Else
s = "[0,0]"
End If
Range("D96").Value = s
but it get only one value, what I should do for continuing.
Any help will be appreciated.
Dim isect As Range
Dim aCell As Range
Dim Output As String
Set isect = Intersect(target, Me.Range("$B$80:$J$80"))
If Not isect Is Nothing Then
For Each aCell In isect
If aCell.Interior.Color = vbYellow Then
Output = Output & "," & aCell.Value
Else
Output = Output & "," & "[0,0]"
End If
Next aCell
Range("D96") = Mid(Output, 2)
End If
Is this what you want?
Dependent on Cell Property (Fill Color)
Standard Module Code (e.g. Module1)
Option Explicit
Function getString(SourceRange As Range, _
Optional ByVal FillColor As Long = 0, _
Optional ByVal CriteriaNotMetValue As Variant = Empty, _
Optional ByVal Delimiter As String = ", ") _
As String
If SourceRange Is Nothing Then Exit Function
' Write values of range to array.
Dim Data As Variant
If SourceRange.Rows.Count > 1 Or SourceRange.Columns.Count > 1 Then
Data = SourceRange.Value
Else
ReDim Data(1 To 1, 1 To 1): Data(1, 1) = SourceRange.Value
End If
' Modify values in array.
Dim i As Long, j As Long, Result As String
For i = 1 To UBound(Data)
For j = 1 To UBound(Data, 2)
If SourceRange.Cells(i, j).Interior.Color <> FillColor Then
Data(i, j) = CriteriaNotMetValue
End If
Result = Result & Delimiter & Data(i, j)
Next j
Next i
' Remove redundant Delimiter.
getString = Right(Result, Len(Result) - Len(Delimiter))
End Function
Sheet Code (e.g. Sheet1)
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
Const rngAddress As String = "B80:J80"
Const cellAddress As String = "D96"
Const CriteriaNotMetValue As Variant = "[0,0]"
Const FillColor As Long = vbYellow
Const Delimiter As String = ","
If Intersect(Me.Range(rngAddress), Target) Is Nothing Then Exit Sub
On Error GoTo clearError
Application.EnableEvents = False
Dim Result As String
Result = getString(Me.Range(rngAddress), FillColor, CriteriaNotMetValue, Delimiter)
Me.Range(cellAddress).Value = Result
CleanExit:
Application.EnableEvents = True
Exit Sub
clearError:
Debug.Print "Run-time error '" & Err.Number & "': " & Err.Description
On Error GoTo 0
GoTo CleanExit
End Sub

Avoid dependant event trigger each other

I have a Worksheet_change in which two events are checked (edits on cells of column C and edits on cells of column D). The problem is that an edit on column C's cells modify the value of column D's cells (and viceversa), so the Worksheet_change is triggered repeatedly and excel eventually crashes.
How can I avoid the problem but maintaining my functionality?
Private Sub Worksheet_Change(ByVal Target As Range)
Dim rgFound As Range
Dim defVal As Range
Dim currParam As Range
Dim currParamDict As Range
Set targ = Intersect(Target, Range("A:A"))
If Not targ Is Nothing Then
With Worksheets("FT_CASE_xx")
For Each defVal In .Range("A2", .Range("A" & Rows.Count).End(xlUp)).Offset(, 1)
Set currParam = defVal.Offset(, -1)
Dim xlFirstChar As String
xlFirstChar = Left$(currParam, 1)
If xlFirstChar = "B" Then
Set rgFound = Worksheets("DEF_BOOLEAN").Range("A:A").Find(currParam.value)
defVal.Offset(, 1).Interior.Color = RGB(230, 230, 230)
defVal.Offset(, 1).Locked = True
defVal.Offset(, 2).Select
With Selection.Validation
.Delete
.Add Type:=xlValidateList, AlertStyle:=xlValidAlertStop, Operator:=xlBetween, Formula1:="TRUE,FALSE"
.IgnoreBlank = True
.InCellDropdown = True
.InputTitle = ""
.ErrorTitle = ""
.InputMessage = ""
.ErrorMessage = ""
.ShowInput = True
.ShowError = True
End With
Else
Set rgFound = Worksheets("DEF_FLOAT").Range("A:A").Find(currParam.value)
defVal.Offset(, 1).Interior.ColorIndex = 0
defVal.Offset(, 1).Locked = False
defVal.Offset(, 2).Locked = False
defVal.Offset(, 1).NumberFormat = "0.000"
defVal.Offset(, 2).NumberFormat = "0.000"
defVal.Offset(, 3).NumberFormat = "0.000"
End If
If rgFound Is Nothing Then
Debug.Print "Name was not found."
Else
If xlFirstChar = "B" Then
Set currParamDict = rgFound.Offset(, 3)
Else
Set currParamDict = rgFound.Offset(, 5)
End If
defVal.value = currParamDict.value
End If
Next defVal
End With
Else
Set targ = Intersect(Target, Range("C:C"))
If Not targ Is Nothing Then
Dim coeffVal As Range
Dim currVal As Range
Dim RequestedVal As Range
With Worksheets("FT_CASE_xx")
For Each coeffVal In .Range("C2", .Range("C" & Rows.Count).End(xlUp))
Set currVal = coeffVal.Offset(, -1)
Set RequestedVal = coeffVal.Offset(, 1)
Set ParamName = coeffVal.Offset(, -2)
Dim xlFirstChar2 As String
xlFirstChar2 = Left$(ParamName, 1)
If ((xlFirstChar2 = "F") And (IsEmpty(coeffVal.value) = False)) Then
RequestedVal.value = coeffVal.value * currVal.value
End If
Next coeffVal
End With
Else
Set targ = Intersect(Target, Range("D:D"))
If Not targ Is Nothing Then
Dim coeffsVal As Range
Dim val As Range
Dim reqVal As Range
Dim Parameter As Range
With Worksheets("FT_CASE_xx")
For Each reqVal In .Range("D2", .Range("D" & Rows.Count).End(xlUp))
Set coeffsVal = reqVal.Offset(, -1)
Set val = reqVal.Offset(, -2)
Set Parameter = reqVal.Offset(, -3)
Dim xlFirstChar3 As String
xlFirstChar3 = Left$(Parameter, 1)
If ((xlFirstChar3 = "F") And (IsEmpty(reqVal.value) = False)) Then
If val.value = 0 Then
coeffsVal.value = reqVal.value
Else
coeffsVal.value = reqVal.value / val.value
End If
End If
Next reqVal
End With
Else
Exit Sub
End If
End If
End If
End Sub
Maybe a different management of target intersection? How?
My favoured method (which can also be useful in other situations) is to create a variable at global or module level (as required) then check this on each run of the code
Private disableEvents as Boolean
Private Sub Worksheet_Change(ByVal Target As Range)
If disableEvents Then Exit Sub
disableEvents=True
<code here>
disableEvents=False
End sub

Resources