Forum Discussion

gms4b's avatar
gms4b
Brass Contributor
Aug 05, 2019

modify VBA code to include dynamic columns instead of static

I have the following code which looks for the word "unknown" in column "4" (of sheet "Raw Data") and then copies the corresponding row of data to a different sheet ("undiluted"). However, sometimes this data is found in different columns (column 3, 5, 6, etc). How would you code this so that it looks for the header ("Sample Type") and uses this column instead of just column "4"?

 

Thanks,


Greg

 

------------------------------------

Private Sub CommandButton1_Click()
a = Worksheets("Raw Data").Cells(Rows.Count, 1).End(xlUp).Row

For i = 2 To a


If Worksheets("Raw Data").Cells(i, 4).Value = "Unknown" Then

Worksheets("Raw Data").Rows(i).Copy
Worksheets("Undiluted").Activate
b = Worksheets("Undiluted").Cells(Rows.Count, 1).End(xlUp).Row
Worksheets("Undiluted").Cells(b + 1, 1).Select
ActiveSheet.Paste
Worksheets("Raw Data").Activate

 

End If
Next

Application.CutCopyMode = False
ThisWorkbook.Worksheets("Raw Data").Cells(1, 1).Select

End Sub

30 Replies

  • gms4b 

    I am still somewhat puzzled as to the reason you turn so readily to VBA for the analysis.  Excel has a huge amount of built-in functionality to support filtering, both as manual operations and automatic, using formulas,

     

    I ran the code in "Example with VBA4.xlsm" this morning and it took 1min20sec to produce 630 rows for 'undiluted' and a further 30 on the 'diluted' sheet.  I am sure there is plenty of room for speed optimisation of the code but I went on to compare that time with that of a formula-based approach.

     

    I happened to be using a machine with Office 365 loaded so, having converted the source data to a table, I used a modern dynamic array formula to extract the required records.  The formulas

    = FILTER( RawData,

    ((RawData[Sample Type]="Unknown") +

    (RawData[Sample Type]="Quality Control")) *

    (RawData[Dilution Factor]=1) )

    and

    = FILTER( RawData,

    ((RawData[Sample Type]="Unknown") +

    (RawData[Sample Type]="Quality Control")) *

    (RawData[Dilution Factor]>1) )

    each took 0.1sec to run and gave identical output.

     

    p.s. Other options would include running the Advanced Filter from code (any version of Excel) or to use Power Query (Office 2010 with add-in, native 2016 and on).

     

    I have now attached a copy of the file with your early VBA, with Power Query and with Dynamic Array formulas.  The last only works with the most up-to-date versions of Office365.

    • gms4b's avatar
      gms4b
      Brass Contributor

      PeterBartholomew1 

       

      Thanks Peter. I'll start to look into this. In the end, the whole process is:

      - dividing Unknown and Quality Control samples between diluted and undiluted groups (Then get rid of columns that I don't need)

      - Adding some columns to each of these sets to do some math do determine if the samples are in a range

      - Then a VLOOKUP to compare samples in the diluted and undiluted groups and choose the proper one for final data. 

       

      This has been a very manual process not just for me, but my teammates. I wanted to make a way for people to simply add in all the raw data into the first tab of a spreadsheet, press a button, and get final data. So, that's why I thought VBA would be a good way to automate the whole process. 

       

      If i can find a way to do the filtering (as you suggest) and replace the first part of the code, then that sounds like a good idea. As you mentioned.....it can be rather slow. And, its possible that 1000's of rows of data could be in a set. 

       

      Thanks,

       

      Greg

       

       

      • PeterBartholomew1's avatar
        PeterBartholomew1
        Silver Contributor

        gms4b 

        Do you and any colleagues that need to manipulate the results have Office 365 and can you set it to monthly updates?  The array methods I have used have only come out in the latest releases of Excel and, though it would be possible to transfer the data using INDEX it would lack elegance. 

        Removing columns could be achieved by being more selective as to the columns brought through in the first place, even down to the level of bring key columns across individually within the downstream calculation.

  • gms4b 

    I suspect that with the newest releases of Office 365 most of the things described here can be done easily with worksheet formulas.  The points to observe are that all raw data should be entered into Excel Tables so that the field to be searched for the value "Unknown" can be identified by name rather than a static numeric index.  Given that tables adjust dynamically to the data, no counts are required to determine the length of the column.

     

    The key function is FILTER which will return all the records that match the criterion

    = FILTER( Table1, Table1[field]="Unknown"} )

     

    The advantage of names is as great when it comes to VBA.  A named field can be block-read into a variant array and then the looping is in memory rather than requiring continual exchanges between the VBA environment and the worksheet.  If the column changes, the Name moves with it, so the code is more robust.

  • Haytham Amairah's avatar
    Haytham Amairah
    Silver Contributor

    gms4b

     

    Hi,

     

    You can do this by a simple algorithm:

    • Search for the header of (Sample Type) in the worksheet using the Cell.Find method in VBA
    • Store the column number of the Sample Type header in a variable called: headerColumn
    • Use this variable in place of the number 4 in the IF statement's logical test

     

    So the code looks like this:

    Sub test()
        Application.ScreenUpdating = False
        
        Dim a As Long
        a = Worksheets("Raw Data").Cells(Rows.Count, 1).End(xlUp).Row
        
        Dim headerColumn As Long
        headerColumn = Cells.Find(What:="Sample Type", After:=Range("A1"), LookIn:=xlFormulas, _
                       LookAt:=xlPart, SearchOrder:=xlByRows, SearchDirection:=xlNext, _
                       MatchCase:=False, SearchFormat:=False).Column
                       
        For i = 2 To a
            If Worksheets("Raw Data").Cells(i, headerColumn).Value = "Unknown" Then
                Worksheets("Raw Data").Rows(i).Copy
                Worksheets("Undiluted").Activate
                Dim b As Long
                b = Worksheets("Undiluted").Cells(Rows.Count, 1).End(xlUp).Row
                Worksheets("Undiluted").Cells(b + 1, 1).Select
                ActiveSheet.Paste
                Worksheets("Raw Data").Activate
            End If
        Next
        
        Application.CutCopyMode = False
        ThisWorkbook.Worksheets("Raw Data").Cells(1, 1).Select
        Application.ScreenUpdating = True
    End Sub

     

    Hope that helps

    • gms4b's avatar
      gms4b
      Brass Contributor

      Haytham Amairah 

       

      oooo. Nice, it works! Thanks!!

       

      Another question? What if I also wanted to look for "Quality Control" in addition to "Unknown"....so, any row that had "Quality Control" or "Unknown" in a column with "Sample Type" as the header would get moved to the "Undiluted" sheet.

       

      Thanks!

       

      Greg

       

      • gms4b's avatar
        gms4b
        Brass Contributor

        gms4b 

        @Haytham Amairah 

         

        Nevermind....figured it out: 

         

        If (Worksheets("Raw Data").Cells(i, headerColumn).Value = "Unknown" Or Worksheets("Raw Data").Cells(i, headerColumn).Value = "Quality Control") Then

         

        Thanks!

Resources