Consulting

Results 1 to 10 of 10

Thread: Printing Unique Values (From a column) in a Combobox

  1. #1

    Printing Unique Values (From a column) in a Combobox

    Hi!

    So, what I'm trying to accomplish is to pull values from a column and store them in a userform-based combobox without any duplicates showing.

    So far, I can accomplish pulling all of the values, including duplicates into the box:
    [VBA]Private Sub UserForm_Initialize()
    Dim wbBook As Workbook
    Dim wsSheet As Worksheet
    Dim rnData As Range
    Dim vaData As Variant

    Set wbBook = ThisWorkbook
    Set wsSheet = wbBook.Worksheets("Scoring")

    With wsSheet
    Set rnData = .Range(.Range("C2"), .Range("C65536").End(xlUp))
    End With

    vaData = rnData.Value

    With Me.TeamDrop
    .Clear
    .List = vaData
    .ListIndex = -1
    End With
    End Sub[/VBA]

    I tried searching up methods to remove duplicates while loading everything into the combobox, although couldn't find anything other than only retrieving 'unique values' upon execution.

    I've attempted using this code:
    [VBA]Sub RemoveDuplicates()
    Dim AllCells As Range, Cell As Range
    Dim NoDupes As New Collection
    Dim i As Integer, j As Integer
    Dim Swap1, Swap2, Item

    Set AllCells = Range("C2:C100")

    On Error Resume Next
    For Each Cell In AllCells
    NoDupes.Add Cell.Value, CStr(Cell.Value)

    Next Cell

    On Error GoTo 0

    For i = 1 To NoDupes.Count - 1
    For j = i + 1 To NoDupes.Count
    If NoDupes(i) > NoDupes(j) Then
    Swap1 = NoDupes(i)
    Swap2 = NoDupes(j)
    NoDupes.Add Swap1, before:=j
    NoDupes.Add Swap2, before:=i
    NoDupes.Remove i + 1
    NoDupes.Remove j + 1
    End If
    Next j
    Next i

    For Each Item In NoDupes
    If Item <> "" Then
    frmTeam.TeamBox.AddItem Item
    End If
    Next Item

    End Sub
    [/VBA]
    although the column-range's values are not listed in the combobox.

    Is there something I'm missing?

    Thank you in advance!

  2. #2
    Knowledge Base Approver VBAX Guru GTO's Avatar
    Joined
    Sep 2008
    Posts
    3,368
    Location
    Hi there,

    Do the values need to be sorted? If so, can the range be sorted, or do we need to sort just the unique values?

    Mark

  3. #3
    Hi Mark,

    The values don't need to be sorted. If they were, only the unique values would need alphabetical sorting, although at this point it isn't a necessity; just getting the code to work would be good enough.

  4. #4
    VBAX Tutor mohanvijay's Avatar
    Joined
    Aug 2010
    Location
    MADURAI
    Posts
    268
    Location
    Try this

    [vba]

    Dim WS_Uni As Worksheet
    Set WS_Uni = ThisWorkbook.Sheets("Scoring")
    Dim List_Cbo
    With WS_Uni

    .Range("c2:c" & .Cells(Rows.Count, 3).End(xlUp).Row).AdvancedFilter xlFilterCopy, , .Range("d2"), True
    List_Cbo = WorksheetFunction.Transpose(.Range("d2:d" & .Cells(Rows.Count, 4).End(xlUp).Row))
    .Range("d2:d" & .Cells(Rows.Count, 4).End(xlUp).Row).Clear

    End With
    TeamDrop.List = List_Cbo
    [/vba]

  5. #5
    Knowledge Base Approver VBAX Guru GTO's Avatar
    Joined
    Sep 2008
    Posts
    3,368
    Location
    Quote Originally Posted by Ealcmhar
    Hi!

    So, what I'm trying to accomplish is to pull values from a column and store them in a userform-based combobox without any duplicates showing.
    Compliments to the clarity in your question

    Try something like -

    In the userform:

    [VBA]Option Explicit

    Private Sub UserForm_Initialize()
    Dim rngLastCell As Range
    Dim aryValues As Variant
    Dim DIC As Object
    Dim n As Long

    With Sheet1 '<---using the sheet's codename.
    '// Ensure that data exists, setting a reference to the last cell with //
    '// anything in it, in C2 and thereafter in the column. //
    Set rngLastCell = RangeFound(.Range(.Cells(2, "C"), .Cells(.Rows.Count, "C")))
    '// In case no data exists, leave a way to skip out. //
    If Not rngLastCell Is Nothing Then
    '// Data exists? Then create and reference a DIctionary Object. //
    Set DIC = CreateObject("Scripting.Dictionary")
    '// Grab the values from the range and plunk into an array. //
    aryValues = .Range(.Cells(2, "C"), rngLastCell).Value
    '// Run through the values. Using the dictionary's keys, we either //
    '// add a new key (for a new value), or just overwrite the already //
    '// created key's (if the key already exists) value in .Item, which //
    '// inour case, is left empty, as we are just using the .Key to //
    '// create a unique list. //
    For n = LBound(aryValues, 1) To UBound(aryValues, 1)
    DIC.Item(aryValues(n, 1)) = Empty
    Next

    '// .Keys is a 1-dimensional array, so Transpose to chunk into .List//
    Me.ComboBox1.List = Application.Transpose(DIC.Keys)
    End If
    End With
    End Sub[/VBA]

    In a Standad Module:

    [VBA]Function RangeFound(SearchRange As Range, _
    Optional ByVal FindWhat As String = "*", _
    Optional StartingAfter As Range, _
    Optional LookAtTextOrFormula As XlFindLookIn = xlValues, _
    Optional LookAtWholeOrPart As XlLookAt = xlPart, _
    Optional SearchRowCol As XlSearchOrder = xlByRows, _
    Optional SearchUpDn As XlSearchDirection = xlPrevious, _
    Optional bMatchCase As Boolean = False) As Range

    If StartingAfter Is Nothing Then
    Set StartingAfter = SearchRange(1)
    End If

    Set RangeFound = SearchRange.Find(What:=FindWhat, _
    After:=StartingAfter, _
    LookIn:=LookAtTextOrFormula, _
    LookAt:=LookAtWholeOrPart, _
    SearchOrder:=SearchRowCol, _
    SearchDirection:=SearchUpDn, _
    MatchCase:=bMatchCase)
    End Function[/VBA]
    Hope that helps,

    Mark

  6. #6
    This suffices:

    [vba]Private Sub UserForm_Initialize()
    ComboBox1.List = Filter([transpose(if(countif(offset(C2,,,row(1:300)),C2:C300)=1,C2:C300))], "False", False)
    End Sub[/vba]

    You can adapt the range to 3000 if necessary.

    In your case:

    [VBA]
    Private Sub UserForm_Initialize()
    TeamDrop.List =Filter([transpose(if(countif(offset(scoring!C2,,,row(1:3000)),scoring!C2:C3000)=1,s coring!C2:C3000))], "False", False)
    End Sub
    [/VBA]



    PS.
    .clear is redundant

    .listindex=-1 is redundant












  7. #7
    VBAX Guru Kenneth Hobs's Avatar
    Joined
    Nov 2005
    Location
    Tecumseh, OK
    Posts
    4,956
    Location
    Typically, I might use a collection or dictionary object method in a function.

    Since I like reusable functions, I worked up a solution using snb's Filter method. As typical in some of my solutions, I left it to the user to insure that a proper value is sent to the function.

    [vba]Private Sub UserForm_Initialize()
    ComboBox1.List = NoDupCol(Worksheets("Scoring").Range("C2", _
    Worksheets("Scoring").Range("C" & Rows.Count).End(xlUp)))
    End Sub

    Function NoDupCol(aRange As Range) As Variant
    Dim w As Worksheet, s As String, q As String
    Set w = aRange.Worksheet
    q = """"
    s = "'" & w.Name & "'!" & aRange.Address(False, False)
    s = "=Transpose(If(CountIf(Offset(" & "'" & w.Name & "'!" & aRange(1).Address(False, False) & _
    ",,,Row(1:" & aRange.Rows.Count & "))," & s & ")=1," & s & "))"
    NoDupCol = Filter(Evaluate(s), False, False)
    End Function[/vba]

  8. #8
    @KH

    Thank you for taking an interest in the method I suggested.

    some observations:

    - you use set w=arange.worksheet and w.name , although you could have used arange.parent.name (no need to create an extra object variable)
    - in
    s = "=Transpose(If(CountIf(Offset(" & "'" & w.Name & "'!" & aRange(1).Address(False, False) & ",,,Row(1:" & aRange.Rows.Count & "))," & s & ")=1," & s & "))"
    you forgot(?) to substitute a string by variable s
    s = "=Transpose(If(CountIf(Offset(s & ",,,Row(1:" & aRange.Rows.Count & "))," & s & ")=1," & s & "))"
    - I'm rather intrigued by your variable 'q'

    Since I have a preference for avoiding variables if they are not absolutely required or evidently beneficial I reworked your function.
    Another personal preference is to use 'cells' instead of 'ranges' when numbers come in.

    [vba]Private Sub UserForm_Initialize()
    ComboBox1.List = NoDupCol(Sheets(1).Cells(2, 3).Resize(Sheets(1).Cells(Rows.Count, 3).End(xlUp).Row))
    End Sub[/vba]

    [vba]Function NoDupCol(aRange As Range) As Variant
    NoDupCol = Filter(Evaluate(Replace("Transpose(If(CountIf(Offset(~,,,Row(1:" & aRange.Rows.Count & ")),~)=1,~))", "~", "'" & aRange.Parent.Name & "'!" & aRange.Address(False, False))), False, False)
    End Function[/vba]

  9. #9
    Generally I prefer the bracketed [ ] evaluate method because of it's readability compared to the 'string' version of evaluate.

    So I came up with:

    [vba]
    Private Sub UserForm_Initialize()
    Sheets(1).Cells(2, 3).Resize(Sheets(1).Cells(Rows.Count, 3).End(xlUp).Row).Name = "snb_"

    ComboBox1.List = Filter([transpose(if(countif(offset(snb_,,,row(offset(snb_,1-row(snb_),0))),snb_)=1,snb_))], False, False)
    End Sub
    [/vba]

    - you can't pass a variable to the bracketed Evaluate method
    - to inform the method what range is involved we produce the object 'name' that can be evaluated by the bracketed method.
    - since changing the Excel environment in a function can produce unexpected results we refrain from using a function here.
    Last edited by snb; 10-13-2012 at 12:37 PM.

  10. #10
    Mac Moderator VBAX Guru mikerickson's Avatar
    Joined
    May 2007
    Location
    Davis CA
    Posts
    2,778
    This will both alphabetize and remove duplicates.

    [VBA]
    Dim dataRange As Range, oneCell As Range
    Dim i As Long
    With Sheet1.Range("A:A")
    Set dataRange = Range(.Cells(1, 1), .Cells(Rows.Count, 1).End(xlUp))
    End With

    With ComboBox1
    For Each oneCell In dataRange

    For i = 0 To .ListCount - 1
    If oneCell.Value < .List(i) Then
    GoTo PlaceItem
    ElseIf oneCell.Value = .List(i) Then
    GoTo NextCell
    End If
    Next i
    PlaceItem:
    .AddItem oneCell.Value, i
    NextCell:
    Next oneCell
    End With
    [/VBA]

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •