I have my column B, starting from cell 2 (because of the header) containing codes of type String. I would like to get a list of these codes. However, these codes are repeated a number of times. So I would like to loop through my column B and add to my array of codes whenever a new one is encountered, if that makes sense.
Here is my code. How can this be done ? Thanks in advance.
Sub List()
Dim listCodes() As String
With Worksheets("My sheet)
nbr_lines = .Rows.Count
Dim i As Long
val_old = .Cells(2, 2).Value
listCodes(1) = val_old
For i = 2 To nbr_lines
val_new = .Cells(i + 1, 2).Value
While val_old = val_new
val_old = val_new
Wend
listCodes(i) = val_new
val_old = val_new
Next i
End With
End Sub
As mentioned in my comment, I'd suggest a dictionary. Drop the entire column into an array for speedy processing first, then throw it all in the dictionary:
Sub List()
Dim listCodes() As Variant
Dim dict As Object: Set dict = CreateObject("Scripting.Dictionary")
Dim nbr_lines As Long
With Worksheets("My sheet")
'Get last used line and throw values in array;
nbr_lines = .Cells(.Rows.Count, 2).End(xlUp).Row
listCodes = .Range("B2:B" & nbr_lines).Value
'Loop array instead of cells for speed. Add all unique items into dictionary;
For Each el In listCodes
dict(el) = ""
Next
'Add the content of the dictionary to the sheet;
.Range("C2").Resize(dict.Count).Value = Application.Transpose(dict.Keys)
End With
End Sub
Note: This can also be achieved outside of VBA through easy formulae like the UNIQUE() function.
You can use dictionary approach. Below sub will copy only unique items to column D. Modify codes as your need.
Public Sub CopyUniqueOnly()
Dim i As Long
Dim currCell As Range, dict As Object
Set dict = CreateObject("Scripting.Dictionary")
With ThisWorkbook.Worksheets("Sheet1")
For Each currCell In .Range("B2", .Cells(.Rows.Count, 2).End(xlUp))
If Not dict.exists(currCell.Value) And Not IsEmpty(currCell) Then
dict.Add currCell.Value, currCell.Value
End If
Next currCell
End With
Range("D2").Resize(dict.Count) = Application.Transpose(dict.keys)
End Sub
Related
I have created a code to use Vlookup formula through VBA but i am stuck that how to fix it. It is very simple to lookup a range but i do not know what to do. Any help will be appreciated.
Sub Example()
Dim value As Range
Dim table As Range
Dim col_index As Range
Dim FinalResult As Variant
lRow = Sheet2.Cells(Rows.Count, 2).End(xlUp).Row
Set value = Sheet2.Range("A2")
Set table = Sheet1.Range("A2:D15")
Set col_index = Sheet2.Range("D2:D" & lRow)
FinalResult = Application.WorksheetFunction.VLookup(value, table, col_index, False)
End Sub
Here is the formula which is working perfectly
=VLOOKUP(Sheet2!A2,Sheet1!$A$2:$D$15,4,FALSE)
Edited
Sub Example()
Dim value As Range
Dim table As Range
Dim col_index As Range
Dim FinalResult As Variant
lRow = Sheet2.Cells(Rows.Count, 2).End(xlUp).Row
Set value = Sheet2.Range("A2")
Set table = Sheet1.Range("A2:D15")
FinalResult = Application.WorksheetFunction.VLookup(value, table, 4, False)
End Sub
Edited but still not working
Sub Example()
Dim rng As Range
Dim table As Range
Dim col_index As Range
Dim FinalResult As Variant
lRow = Sheet2.Cells(Rows.Count, 2).End(xlUp).Row
Set rng = Sheet2.Range("A2")
Set table = Sheet1.Range("A2:D15")
FinalResult = Application.WorksheetFunction.VLookup(rng, table, 4, False)
rng.value = FinalResult.value
End Sub
Assume you have two tables.
Your data table in "Sheet1"
Your output table where you want the result ("Sheet2").
To get the countries from the data sheet ("Sheet1") you would use the formula (I use ";" as separator as I use nordic version of excel):
=VLOOKUP(Sheet2!A2,Sheet1!$A$2:$D$15,4,FALSE)
So in VBA this would look like this:
Sub Example()
Dim tbl As Range
Dim col_index As Range
Dim Lookup_val As Long 'if you use a numerical number as in my example
'Dim Lookup_val As String 'if you use a text or words + numbers as lookup criteria
Dim FinalResult As Variant 'I would consider to use string or long... more specific declaration if you know the datatype to be retrieved.
lRow = Sheet2.Cells(Rows.Count, "B").End(xlUp).Row 'End row in the column we should WRTIE the answer from the vlookup function
EndRow = Sheet2.Cells(Rows.Count, "A").End(xlUp).Row 'End row of the range we would like to MATCH values
Set tbl = Sheet1.Range("A2:D15") 'Data table range
For i = 2 To EndRow 'What range we should loop through. We want to loop from row 2 until the last row in Column A for Sheet2
Lookup_val = Sheet2.Cells(i, "A").value 'Value that should be use as lookup value in the "vlookup function"
FinalResult = Application.WorksheetFunction.VLookup(Lookup_val, tbl, 4, False) 'Perform the vlookup function
Sheet2.Cells(i, "B").value = FinalResult 'write the result of the vlookup finding
Next i 'check next row (go to next lookup value)
End Sub
If the vlookup function can't match a value in the table it will give you an error. I usually fix it dirty by wrapping the function in a error handling line and you need to clear the FinalResult value for each iteration, i.e.:
For i = 2 To EndRow
FinalResult = "" 'To clear previous value from loop iteration
Lookup_val = Sheet2.Cells(i, "A").value
On Error Resume Next 'ignore error if no value found
FinalResult = Application.WorksheetFunction.VLookup(Lookup_val, tbl, 4, False)
On Error GoTo 0 'continue loop anyway
Sheet2.Cells(i, "B").value = FinalResult
Next i
The below code basically looks at a source sheet on workbook open, takes the values from a range and loops through adding each value to a combobox.
What I want to do is include some code to ensure only unique values, i.e. no dupes, are added.
Any ideas how I can get that working?
Thanks!
Private Sub Workbook_Open()
Dim wb As Workbook
Set wb = ThisWorkbook
Dim Home As Worksheet
Dim Datasource As Worksheet
'Define Variables and dropdown object
Dim LastRow As Long
Dim MIDCell As Range
Dim ComboMID As ComboBox
Set Home = ActiveSheet
Set Home = Worksheets("UPDATER")
Set Datasource = wb.Sheets("LaunchCodes")
'asign dropdown object to combobox
Set ComboMID = Home.OLEObjects("ComboBox1").Object
'Empty the combobox currnetly to avoid duplicating content
ComboMID.Clear
'With and For loop to put all values in games launch code column, ignoring any blanks, into combobox
With Datasource
LastRow = .Cells(.Rows.Count, "D").End(xlUp).Row
For Each MIDCell In .Range("D2:D1000" & LastRow)
If MIDCell.Value <> "" Then
ComboMID.AddItem MIDCell.Value
End If
Next
End With
End Sub
The code below avoids looping through cells in a worksheet because it's slow. Actually, that process can be sped up by reading the list into a variable (as, in fact, my code also does) but using Excel's own RemoveDuplicates method appears more efficient.
Private Sub Workbook_Open()
' 155
Dim Wb As Workbook
Dim ComboMid As ComboBox
Dim TmpClm As Long ' number of temporary column
Dim Arr As Variant ' unique values from column D
Set Wb = ThisWorkbook
With Wb.Worksheets("UPDATER")
Set ComboMid = .OLEObjects("ComboBox1").Object
With .UsedRange
TmpClm = .Column + .Columns.Count
End With
End With
With Wb.Sheets("LaunchCodes")
' create a copy of your data (without header) in an unused column
.Cells(2, "D").CurrentRegion.Copy .Cells(1, TmpClm)
.Cells(1, TmpClm).CurrentRegion.RemoveDuplicates Columns:=1, Header:=xlNo
Arr = .Cells(1, TmpClm).CurrentRegion.Value
.Columns(TmpClm).ClearContents
End With
With ComboMid
.List = Arr
.ListIndex = 0 ' assign first list item to Value
End With
End Sub
You don't need to clear the combo box in the above code because replacing the List property with a new array automatically removes whatever it was before.
Unique to ComboBox
To learn about the combo box study this.
You can replace the code after the line Set ComboMID = Home.OLEObjects("ComboBox1").Object with the following snippet:
Dim rng As Range
With DataSource
LastRow = .Cells(.Rows.Count, "D").End(xlUp).Row
Set rng = .Range("D2:D" & lastrow)
End With
Dim Unique As Variant
Unique = getUniqueFromRange(rng)
If Not IsEmpty(Unique) Then
ComboMID.List = Unique
End If
which uses the following function:
Function getUniqueFromRange( _
rng As Range) _
As Variant
If rng Is Nothing Then
Exit Function
End If
Dim Data As Variant
If rng.Cells.CountLarge > 1 Then
Data = rng.Value
Else
ReDim Data(1 To 1, 1 To 1)
Data(1, 1) = rng.Value
End If
cCount = UBound(Data, 2)
Dim cValue As Variant
Dim i As Long
Dim j As Long
With CreateObject("Scripting.Dictionary")
.CompareMode = vbTextCompare
For i = 1 To UBound(Data, 1)
For j = 1 To cCount
cValue = Data(i, j)
If Not IsError(cValue) And Not IsEmpty(cValue) Then
.Item(cValue) = Empty
End If
Next j
Next i
If .Count > 0 Then
getUniqueFromRange = .Keys
End If
End With
End Function
i need help with dictionary understanding so im trying with something simple. I have code that search and delete duplicate value.
I store dict Key as People and item as ID's. Idea is to loop to cell range with data, find duplicate values delete them but concatenate Item(ID's).
How can i get item from Dictionary to range cell with ID's and concatenate values? I wolud appreciate and help, link, tutorial, suggestion
Code so far:
Option Explicit
Sub DictionaryTest()
Dim dict As Scripting.Dictionary
Dim rowCount As Long
Dim People As String
Dim ID As Integer
Dim item As Variant
Set dict = New Scripting.Dictionary
rowCount = Cells(Rows.Count, "E").End(xlUp).Row
'Debug.Print rowCount
Do While rowCount > 1
People = Sheet2.Cells(rowCount, "E").Value
ID = Sheet2.Cells(rowCount, "D").Value
If dict.Exists(People) Then
'Sheet2.Rows(rowCount).EntireRow.Delete
Else
dict.Add People, ID
End If
rowCount = rowCount - 1
Loop
End Sub
Thank you!
Instead of storing the ID value in the dictionary, you can reference the ID cell and concatenate the values there.
Dim idCell As Range, r As Long
'...
'...
For r = rowCount to 2 Step - 1
People = Sheet2.Cells(rowCount, "E").Value
Set idCell = Sheet2.Cells(rowCount, "D")
If dict.Exists(People) Then
With dict(People) '<< first id cell...
.Value = .Value & ";" & IdCell.Value
End With
Sheet2.Rows(rowCount).EntireRow.Delete 'get id *before* delete ;-)
Else
dict.Add People, idCell 'reference first ID cell (the cell
' itself, not the cell value)
End If
Next r
Please see if this works for you.
Sub RemDupVal()
Dim t As Range, x As Range, z As Range
Set x = Range("A2:A7") 'ID
Set z = Range("B2:B7") 'Item
Set t = Cells(2, Cells(1, 16383).End(xlToLeft).Column + 1).Resize(x.Rows.Count)
t = x.Parent.Evaluate(x.Address & "&" & z.Address) 'assuming evaluate character limit is met
Union(x, t).Select
selection.RemoveDuplicates t.Column, xlNo
t.ClearContents: Cells(1, 1).Select
End Sub
I'm working on this code and update it a bit. I see that I Immediate window Items from duplicate dictionary are concatenate so code work exactly what I want but I don't know how I can get that value concatenate in cells. In dictionary keys are People and Item are ID
This is best result I have in many code testing.
Sub DictTest()
Dim dict As Scripting.Dictionary
Dim rowsCount As Long
Dim People As String, id As Integer
Set dict = New Scripting.Dictionary
rowsCount = Cells(Rows.Count, "D").End(xlUp).Row
People = Sheet2.Cells(rowsCount, "D").Value
Do While rowsCount > 1
People = Sheet2.Cells(rowsCount, "D").Value
id = Sheet2.Cells(rowsCount, "C")
'if duplicate value is found then concatenate Item value
If dict.Exists(People) Then
dict(People) = dict(People) & "," & " " & id
Debug.Print dict(People) '-> in immediate window shows concatenate Item values
Sheet2.Rows(rowsCount).EntireRow.Delete
Else
dict.Add People, id
End If
rowsCount = rowsCount - 1
Loop
End Sub
I want to populate unique values into combobox.
My sheet details
Code:
Private Sub ComboBoxscname_DropButtonClick()
With Worksheets("A1")
ComboBoxscname.List = .Range("B2:B" & .Cells(.Rows.Count, "A").End(xlUp).Row).Value
End With
End Sub
I have highlighted with yellow which are duplicated for column "B" and should be displayed only once in combobox.
Another solution I have but getting error when selecting specific sheet name.
Sub ComboBoxscnameList()
Dim LR As Long
Dim ctrl As Object
'Set ctrl = Sheets("A1").Select
LR = Cells(Rows.Count, "B").End(xlUp).Row
ctrl.List() = CreateArray(Range("B2:B" & LR))
End Sub
'creates an array from a given range
'ignores blanks and duplicates
Function CreateArray(r As Range)
Dim col As New Collection, c As Range, TempArray(), i As Long
'for each cell in range r
For Each c In r
On Error Resume Next
col.Add c.Value, CStr(c.Value)
If Err.Number = 0 And Trim(c) <> "" Then
ReDim Preserve TempArray(i)
TempArray(i) = c.Value
i = i + 1
End If
Err.Clear
Next
CreateArray = TempArray
Erase TempArray
End Function
Private Sub ComboBoxscname_DropButtonClick()
Call ComboBoxscnameList
End Sub
The easiest way to save a unique set of values from a Column or Range is by using a Dictionary. You loop though your cells in column B, and check if each one is already in the Dictionary keys, the syntax is Dict.Exists("your_parameters").
You can read more about using Dictionary HERE.
Review the modified code below, you want to add it to your UserForm_Initialize() event.
Modified Code
Private Sub UserForm_Initialize()
Dim i As Long, ArrIndex As Long, LastRow As Long
Dim Dict As Object, Key As Variant
Dim HSNArr() As String
Application.ScreenUpdating = False
' us a Dictionary, and save unique Eco-System as array
Set Dict = CreateObject("Scripting.Dictionary")
With ThisWorkbook.Worksheets("Sheet2") ' <-- modify to your sheet's name
LastRow = .Cells(.Rows.Count, "B").End(xlUp).Row
ReDim HSNArr(1 To LastRow) ' redim HSN array >> will optimize size later
ArrIndex = 1
For i = 2 To LastRow
If Not Dict.Exists(.Range("B" & i).Value2) And Trim(.Range("B" & i).Value2) <> "" Then ' make sure not in Dictionary and ignore empty cells
Dict.Add .Range("B" & i).Value2, .Range("B" & i).Value2 ' add current HSN
HSNArr(ArrIndex) = .Range("B" & i).Value2
ArrIndex = ArrIndex + 1
End If
Next i
End With
ReDim Preserve HSNArr(1 To ArrIndex - 1) ' resize to populated size of Array
Application.ScreenUpdating = True
With Me.ComboBoxscname
.Clear ' clear previous combo-box contents
For i = 1 To UBound(HSNArr) ' loop through array, add each unique HSN to Combo-Box
.AddItem HSNArr(i)
Next i
' show default value
.Value = HSNArr(1)
End With
End Sub
I would like to extract unique values based on a criteria. This is what I have so far:
Sub test()
Dim x As Variant
Dim objdict As Object
Dim lngrow As Long
With Sheets("Sheet1")
Set objdict = CreateObject("Scripting.Dictionary")
x = Application.Transpose(.Range("A1", .Range("A1").End(xlDown)))
For lngrow = 1 To UBound(x, 1)
objdict(x(lngrow)) = 1
Next
.Range("C1:C" & objdict.Count) = Application.Transpose(objdict.keys)
End With
End Sub
Below what I would like to achieve:
As you can see the values are in column A, the criteria are in column B and the unique values are in column C. Can anyone show me what I need to change in my code?
You're almost there! See the comments in the code below:
Option Explicit
Sub Test()
Dim x As Variant
Dim objDict As Object
Dim lngRow As Long
Set objDict = CreateObject("Scripting.Dictionary")
With Sheet1 '<== You can directly use the (Name) property of the worksheet as seen from the VBA editor.
x = .Range(.Cells(1, 1), .Cells(.Rows.Count, 2).End(XlDirection.xlUp)).Value
For lngRow = 1 To UBound(x, 1)
If x(lngRow, 2) = 1 Then '<== Example criteria: value is 1 in column B.
objDict(x(lngRow, 1)) = 1 '<== Don't know if this is significant in your case (I typically assign True).
End If
Next
.Range(.Cells(1, 3), .Cells(objDict.Count, 3)).Value = Application.Transpose(objDict.Keys)
End With
'Cleanup.
Set objDict = Nothing
End Sub
Note that I've replaced the strings used within Range() by the numeric indices (row, column), which IMHO is better practice.