0

I have the following case:

1.Column D populated with about 100 values,
2. Using these I create a validation in the Column A cells
3. If I have a value in Cell "A1", this particular value should not appear 
   in Cell "A2" dropdown list, now the values in "A1" and "A2" should not appear in "A3" and so on.

What should be the thought process to write the VBA code for this?

There shouldn't be one in the dropdown list now.

4
  • Where does column D come in? Do you mean, A1 should not contain D1, and A2 should not contain D1 or D2, etc ? Commented Oct 31, 2017 at 10:32
  • Column D is simply a validation list. Nothing like that. If from the dropdown of A1, I select "D3"'s value, then A2 should not show the "D3"s value at all. In essence, the column A will have only unique values. Commented Oct 31, 2017 at 10:52
  • So, each cell is column A is a dropdown control..?. A1's dropdown's value list is {w,x,y,z} and if "x" is selected then A2's dropdown value list is {w,y,z}, etc?? Commented Oct 31, 2017 at 10:57
  • 1
    FYI, you don't even need code for that: contextures.com/xlDataVal03.html Commented Oct 31, 2017 at 12:24

3 Answers 3

2

I found this one interesting, so check this out... Should work as you expect it... Post this code into your Worksheet and adapt it for your needs (if necessary). Hope it helps.

Option Explicit

Private Sub Worksheet_Change(ByVal Target As Range)
    Dim ws As Worksheet
    Dim dict As Object
    Dim dictAlreadyTaken As Object
    Dim valueRange As Range
    Dim targetRange As Range
    Dim cell As Object
    Dim Key As Variant
    Dim currentList() As Variant
    Dim i As Integer

    If Target.Column = 1 Then
        Set ws = Worksheets(1)
        Set dict = CreateObject("Scripting.Dictionary")
        Set dictAlreadyTaken = CreateObject("Scripting.Dictionary")
        Set valueRange = ws.Range("D:D")
        Set targetRange = ws.Range("A:A")

        For Each cell In valueRange
            If cell.Value <> "" Then
                dict.Add cell.Value, cell.Row
            Else
                Exit For
            End If
        Next cell

        For Each cell In targetRange
            If cell.Row <= dict.Count Then
                If cell.Value <> "" Then
                    'ad the value taken
                    dictAlreadyTaken.Add cell.Value, cell.Row
                End If
            Else
                Exit For
            End If
        Next cell

        For Each cell In targetRange
            If cell.Row <= dict.Count Then
                'add this list
                Erase currentList
                ReDim currentList(0)
                i = 0
                ws.Cells(cell.Row, 1).Validation.Delete
                For Each Key In dict.keys
                    If Not dictAlreadyTaken.exists(Key) Then
                        i = i + 1
                        ReDim Preserve currentList(i) As Variant
                        currentList(i) = Key
                    End If
                Next Key
                If UBound(currentList) > 0 Then
                    ws.Cells(cell.Row, 1).Validation.Add Type:=xlValidateList, AlertStyle:=xlValidAlertStop, Operator:=xlBetween, Formula1:=Join(currentList, ",")
                End If
            Else
                Exit For
            End If
        Next cell
    End If
End Sub
Sign up to request clarification or add additional context in comments.

5 Comments

I used 2 dictionaries, 1 for all elements, 1 for the elements already taken. because the actual values are used as keys in the dictionary it's easy to check if they already exist. Then an array gets built for every dropdown (without the values already taken). Then the dropdown gets built by using this array... hope that's clear enough
Thank you first of all. Ya, I get the concept..It works! but the problem is that, if the user clicks on the non-empty cell again, all the values in column D are shown, though on clicking.. it throws an error. Am I clear?
The code is edited now... try it again. I just fill the already-taken dictionary now before i update the dropdowns...
Hey..It is..until I fill in the last value from the list. It throws an error then.
Edited... I think this will work. The array is now getting checked. If it is empty it fills in nothing --> No more dropdown. (When you delete a value the dropdowns are back)
1

My thought process would be:

  1. First loop to list all the ranges we need to compare:

    • Cells(1,1) should not appear in Range(Cells(1,4),Cells(1,4))

    • Cells(2,1) should not appear in Range(Cells(1,4),Cells(2,4))

    • Cells(3,1) should not appear in Range(Cells(1,4),Cells(3,4)) ...etc...

  2. Easy enough. Now that we know what ranges to compare, loop through the comparisons:

    • re: Cells(3,1) should not appear in Range(Cells(1,4),Cells(3,4)) :

.

Dim c as range
For Each c in Range(Cells(1,4),Cells(3,4))
If c.Value = Cells(1,4).Value then
    'it's a match! Delete it (or whatever)
    c.Value = ""
End If
Next c

Finally, put the two loops together...


From what I understand of your description, I came up with this:

Sub compareCells()

    Dim c As Range, x As Integer
    For x = 1 To 10
        Debug.Print "Cells(" & x & ",1) should not appear in Range(Cells(1,4),Cells(" & x & ",4))"

        For Each c In Range(Cells(1, 4), Cells(x, 4))

            Debug.Print "compare " & Cells(x, 1).Address & " to " & c.Address

            If Cells(x, 1).Value = c.Value Then
                Cells(x, 1).Cells.Font.Color = vbBlue
            End If

        Next c
    Next x

End Sub

It should be easily adaptable to your needs, or if not, there are plenty of existing solutions & resources, even a Stack Overflow tag:

4 Comments

@Muskaan please read my comment above (with example as I understand your need)
I thing you are heading in some other direction. :P Please see the attached image. I don't want "1" to be in the dropdown now because it has already been chosen previously.
@Muskaan -- I think I'm gettin' ya ... See this image. Besides the actual drop-down... column B lists the allow options in column A's drop-down. So when bbb is selected in dropdown A2, that option is no longer allowed in dropdown A3, etc. Correct? :-)
Exactly! And when I deleted 'bbb' from A2, this should be available in next dropdowns.
0

Here is an approach:

Select a column in your sheet that you can use for a named range (this column can be hidden). For the purpose of example below, I've used column J and my named range is called ValidationRange. I have also assumed that the values in your worksheet start from row 2.

Now in a module, add the following sub:

Sub SetDropDownRange()

    Dim oNa As Name: Set oNa = ThisWorkbook.Names.Item("ValidationRange")
    Dim iLR&, iC&, iLRJ&
    Dim aDRange As Variant
    Dim aVRRange As Variant

    With ThisWorkbook.Worksheets("Sheet12")
        iLR = .Range("D" & .Rows.count).End(xlUp).Row
        iLRJ = .Range("J" & .Rows.count).End(xlUp).Row

        aDRange = Range("D2:D" & iLR)

        For iC = LBound(aDRange) To UBound(aDRange)

            If Len(Trim(aDRange(iC, 1))) <> 0 Then

                If Application.WorksheetFunction.CountIf(Range("A:A"), aDRange(iC, 1)) = 0 Then

                    If IsArray(aVRRange) Then
                        ReDim Preserve aVRRange(UBound(aVRRange) + 1)
                    Else
                        ReDim aVRRange(0)
                    End If

                    aVRRange(UBound(aVRRange)) = aDRange(iC, 1)

                End If

            End If

        Next

    End With

    Range("J2:J" & iLRJ).Value = ""

    Range("J2:J" & UBound(aVRRange) + 2).Value = Application.Transpose(aVRRange)

    oNa.RefersTo = oNa.RefersToRange.Resize(UBound(aVRRange) + 1, 1)

End Sub

Now call this function when something changes in your worksheet.. like so:

Private Sub Worksheet_Change(ByVal Target As Range)
    If Target.Column = 1 Or Target.Column = 4 Then
        SetDropDownRange
    End If
End Sub

Set Data Validation for the cells in column A using the named range (which is ValidationRange for this example)

Now everytime your select a value in column A, it will remove that value from the named range and hence from your dropdown box

6 Comments

Hey! Thanks for your time. Do you want me to create a ValidationRange name list beforehand?
Yes. Once you have 'ValidationRange', UDF will update it as needed
Yep. I did that. BTW what are you doing with column L here? And sheet12 is a typo right? :P It's not working, no output anywhere. :|
'L' was the column I was using when testing the UDF. I have updated the code to get last row for column J. Sheet12 was the sheet I was using for testing as well. You should change that to whatever your sheet name is. Try that and let me know if that works
No, I am still not able to get it this way. :/
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.