Home > Net >  Excel VBA - Find column, then search found column
Excel VBA - Find column, then search found column

Time:12-03

I'm new to Excel VBA, and after quite some time attempting to solve my issue, I am unable to create a working solution. The attached image is a mock up of an actual table I'm working with. I would like to:

enter image description here

#1 Define a date in the VBA to search for in the blue row (e.g. 05/12/2022)

#2 Once found, find all values of both 'Apple' and 'Pear' in that yellow column (Apple = 4 times, Pear = 1 time)

#3 Look at the Green column, and store the names for all matches for 'Apple' in one array (later to be used in a string), and all matches for 'Pear' in another array

#4 Input a comma delimited return of both arrays into a cell within the spreadsheet

Step #1 was completed successfully using the following code:

Public Sub MyVBA()

    Dim c As Range
    Dim colNum As Integer
    Dim wkb As Excel.Workbook
    Dim wks As Excel.Worksheet

    Set wkb = Excel.Workbooks("MyOtherWorkbook.xlsx")
    Set wks = wkb.Worksheets("SheetInWorkbook")
        
    For Each c In wks.Range("1:1")
        If c.Value = "05/12/2022" Then
            colNum = c.Column
        End If
    Next c

End Sub

Step #2 attempt:

    For Each c In wks.Columns(colNum)
        If c.Value = "Apple" Then
            MsgBox "Apple is " & c.Address
        End If
    Next c

This is one of various attempts I've made at Step #2, but each time it produces errors. Advice on how to go forward with Step #2 and #3 would be appreciated.

CodePudding user response:

I've wrote something but you really need to see how its laid out on the excel file which you can find here (obviously download and run, it wont work in google sheets).

https://drive.google.com/file/d/1lSLtyYWAMVb4QM52oItbe4yK74ezquPv/view?usp=sharing

You list the data in worksheet "data", the search values (apples, pears) in worksheet "search" - then it writes them to a new worksheet called output

To run, click the big "RUN" button on the data worksheet, or run the sub.

With a little tinkering you can probably make it exactly how you want, as I didn't 100% understand your question.

Public dataWs As Worksheet
Public outputWs As Worksheet
Public searchWs As Worksheet


Function create_date(string_date)
    'create a date date not a string date
    Dim day, month, year As String
    Dim dte As Date
    
    day = Left(string_date, 2)
    month = Mid(string_date, 4, 2)
    year = Right(string_date, 2)
    dte = DateSerial(Int(year), Int(month), Int(day))
    create_date = dte
    
End Function

Function clear_outout()

    'clear output worksheet

    outputWs.Range("a1:z99999").Clear


End Function

Function addData(name, object)


    Dim x, lr As Integer
    
    lr = outputWs.Cells(Rows.Count, 1).End(xlUp).Row
    
    'add columns headers if not there
    If lr = 1 Then
        outputWs.Cells(1, 1) = "NAME"
        outputWs.Cells(1, 2) = "OBJECT"
        outputWs.Cells(1, 3) = "TIMES"
    End If
    
    For x = 2 To lr   1
        If x = lr   1 Then
            'if not in list add name object
            outputWs.Cells(x, 1) = name
            outputWs.Cells(x, 2) = object
            outputWs.Cells(x, 3) = 1
            Exit For
        ElseIf outputWs.Cells(x, 1) = name And outputWs.Cells(x, 2) = object Then
            ' if in list increment count
            outputWs.Cells(x, 3) = outputWs.Cells(x, 3)   1
            Exit For
        End If
    Next x

End Function

Function check_search_list(search_val)

    ' checks to see if input value is a match with one listed in range 

    Dim search_lr As Integer
        
        'this is the search last row
        search_lr = searchWs.Cells(Rows.Count, 1).End(xlUp).Row
        'loop each search val
        For x = 2 To search_lr
            If searchWs.Cells(x, 1) = search_val Then
                check_search_list = True
            End If
        Next x
        
End Function


Sub check_data()
    ''''
    ''' Run the thing
    ''''

    'set the worksheets
    Set dataWs = Worksheets("data")
    Set outputWs = Worksheets("output")
    Set searchWs = Worksheets("search")
    
    Dim x, y, z, lr, lc As Integer
    Dim searchDate As String
    Dim found_column As Boolean
    
        
        'clear output sheet
        clear_outout
        'this gets the pos of the last filled column to the left
        lc = dataWs.Cells(1, Columns.Count).End(xlToLeft).Column
        'get last row
        lr = dataWs.Cells(Rows.Count, 1).End(xlUp).Row
        'get the date from the user
        searchDate = InputBox("Whats the date in format dd/mm/yyyy")
        'create flag for date found
        found_column = False
        'loop columns and look for dates (as proper dates and not strings)
        For y = 2 To lc
            'if found then add all columns - this compares the date object, not strin g
            If create_date(dataWs.Cells(1, y)) = create_date(searchDate) Then
                found_column = True
                'loop eaach row
                For x = 2 To lr
                    If check_search_list(dataWs.Cells(x, y)) Then
                        ' add the data if search value found
                        addData dataWs.Cells(x, 1), dataWs.Cells(x, y)
                    End If
                Next x
                'end loop as column already found
                Exit For
            End If
        Next y
        
        'open data if found else show message
        If found_column Then
            outputWs.Activate
        Else
            MsgBox "Date not found", vbCritical
        End If
    
End Sub

CodePudding user response:

See the comments inside the code for the description of how this works.

There is 1 main Sub, and two helper Functions. If I were expanding this project I would also split parts of the main Sub into more Functions, to prevent this from getting too messy. For the sake of simplicity in this answer, I kept a lot in the main Sub.

Public Sub MyVBA()

    Dim c As Range
    Dim colNum As Long
    Dim wkb As Excel.Workbook
    Dim wks As Excel.Worksheet

    Set wkb = Excel.Workbooks("MyOtherWorkbook.xlsx")
    Set wks = wkb.Worksheets("SheetInWorkbook")
    
    'Get a Date as input from the user
    Dim UserDate As Date: UserDate = GetUserDate()
    'Exit if the user has declined to input
    If UserDate = 0 Then Exit Sub
    
    'Search for the last filled row and column
    'This can be used to trim the loops so we aren't iterating through a million empty cells
    Dim LastRow As Long
    LastRow = wks.Columns(1).Rows(wks.Rows.Count).End(xlUp).Row
    
    Dim LastColumn As Long
    LastColumn = wks.Rows(1).Columns(wks.Columns.Count).End(xlToLeft).Column
    
    'For each cell in Row 1
    For Each c In wks.Range("1:1").Resize(, LastColumn).Cells
        'if the cell contains a date & the date matches the user input
        If IsDate(c.Value) Then
            If CDate(c.Value) = UserDate Then
                colNum = c.Column
                'if the column is found, stop searching
                Exit For
            End If
        End If
    Next c
    'Exit if Column not found
    If colNum = 0 Then Exit Sub
    
    'KeyRanges is a dictionary, this is an object that holds Key & Item pairs
    'There is an entry in the dictionary for each keyword
    'The entry's Key is the Keyword (Apple or Pear), and the item is a Collection of worksheet ranges where that keyword was found
    Dim KeyRanges As Object
    Set KeyRanges = CreateObject("Scripting.Dictionary")
    
    'List of KeyWords
    Dim KeyWords() As String: KeyWords = Split("Apple,Pear", ",")
    
    'Adding an entry to the dictionary for each keyword
    Dim KeyWord As Variant
    For Each KeyWord In KeyWords
        KeyRanges.Add KeyWord, New Collection
    Next
    
    'search the column for matches
    For Each c In wks.Columns(colNum).Resize(LastRow).Cells
        'compare the cell value to each keyword
        For Each KeyWord In KeyWords
            If c.Value = KeyWord Then
                'If the cell value matches one of the keywords
                'Go into the dictionary entry for that keyword
                'and save the cell from this row, in column A, into the collection
                KeyRanges(KeyWord).Add c.EntireRow.Cells(1)
            End If
        Next
    Next c
    
    'From your example for 05/12/2022
    'KeyRanges now contains 2 entries
        'KeyRanges("Apple") contains a Collection
            'The Collection contains 4 items
                'Range("A2")
                'Range("A5")
                'Range("A7")
                'Range("A8")
        'KeyRanges("Pear") contains a Collection
            'The Collection contains 1 item
                'Range("A4")
    
    'Concatenate into CSV
    'CSVs is an array to contain the CSV for each KeyWord
    Dim CSVs() As String
    ReDim CSVs(UBound(KeyWords))
    
    'For each KeyWord
    Dim i As Long
    For i = 0 To UBound(KeyWords)
        'Take the collection from each entry in KeyRanges
        'Give it to a function which can turn collections into CSVs
        CSVs(i) = JoinCollection(KeyRanges(KeyWords(i)))
    Next
    
    'Join all the CSVs into a single CSV & Output to Worksheet
    Range("A1").Value = Join(CSVs, ",")
    
End Sub
Function GetUserDate() As Date
    'Get Date From User
    Dim UserInput As String
    Do
        UserInput = Application.InputBox(Prompt:="Date:", Default:=Date, Type:=2)
        If UserInput = "" Then
            'User declined to input
            Exit Function
        ElseIf Not IsDate(UserInput) Then
            'User input not valid
            UserInput = ""
            MsgBox "Please enter a valid date.", vbOKOnly, "Error"
        End If
    Loop While UserInput = ""
    
    GetUserDate = CDate(UserInput)
End Function
Function JoinCollection(Col As Collection, Optional Delimiter As String = ",") As String
    If Col.Count = 0 Then Exit Function
    Dim ReturnString As String
    ReturnString = Col(1)
    If Col.Count > 1 Then
        Dim i As Long
        For i = 2 To Col.Count
            ReturnString = ReturnString & Delimiter & Col(i)
        Next
    End If
    JoinCollection = ReturnString
End Function
  • Related