Forum Discussion

Antonino2023's avatar
Antonino2023
Brass Contributor
May 18, 2023
Solved

VBA or Power Query help: Iterate copy/paste from one sheet to another and save each iteration

Hello, 

 

I have 3 sheets within a workbook for which I am trying to establish an "iterative connection". The relevant names are listed below:

  • Workbook name: Test_Iterations
  • Sheet 1 name: All Inputs
  • Sheet 1 Table Name: Inputs
ModelColorWeightVolumeChange Group
59603Blue1738459603,97196,41444,72306,61772
97196Blue9763759603,97196,41444,72306,61772
41444Red9327559603,97196,41444,72306,61772
72306Red4261859603,97196,41444,72306,61772
61772Green3978259603,97196,41444,72306,61772

 

  • Sheet 2 name: Analysis
  • Sheet 2 Table Name: Analysis_T
ModelColorWeightVolumeChange GroupPartsCalculation 1Calculation 2Calculation 3Calculation 4Calculation 5Calculation 6Result 1Result 2Result 3
XXXXX          
               
               
               

 

  • Sheet 3: Summaries
ModelPartsResult 1Result 2Result 3
     
     
     
     

 

My simplified use-case is the following:

Each row in "Inputs" is a set of data that I want to paste into "Analysis_T" in the first row (in the columns that match the same name). It needs to only happen in the first row of "Analysis_T". I have designated the cells that the row data should be copied into with "X".

"Analysis_T" performs calculations that take between 4 and 10 seconds to complete. When they complete, I want all the rows in "Analysis_T" to be copied into sheet "Summaries" for only the columns shown above. (I do not know if calculation time is a factor that needs to be considered). Finally, I want to keep only rows whose value in 'Result 2" is equal to "Yes".

 

I want this action to be repeated for every row in "Inputs". To summarize, each iteration should do the following:

For i = 1 to i = # of rows in "Inputs"

  1. Copy row i 
  2. Clear cells in "Analysis_T" (denoted by "X" in this post)
  3. Paste row i into row 1 of "Analysis_T" (in respective columns)
  4. Wait for data in columns "Result 1"-"Result 3" to populate (if applicable)
  5. Copy all rows of "Analysis_T" (only columns shown above) and paste them into sheet "Summaries". (it should add to the existing data, not overwrite)
  6. Delete all rows whose value in "Result 2" is equal to "No".

End

 

May someone please help me get started with this task? I have few experience with VBA code and Power Query but understand code (background with Python and Matlab).

 

I am not sure if "copy/paste" are the actions that need to occur, but they are what is currently used to manually run this process.

 

 

  • Antonino2023 

    You can started with the VBA code to achieve your task.

    Here's an example VBA code that you can use as a starting point:

     

    Sub IterateAndCopyPaste()
        Dim wb As Workbook
        Dim wsInputs As Worksheet
        Dim wsAnalysis As Worksheet
        Dim wsSummaries As Worksheet
        Dim tblInputs As ListObject
        Dim tblAnalysis As ListObject
        Dim tblSummaries As ListObject
        Dim inputRow As ListRow
        Dim analysisRow As ListRow
        Dim lastRow As Long
        Dim i As Long
        
        ' Set references to the workbook and worksheets
        Set wb = ThisWorkbook
        Set wsInputs = wb.Worksheets("All Inputs")
        Set wsAnalysis = wb.Worksheets("Analysis")
        Set wsSummaries = wb.Worksheets("Summaries")
        
        ' Set references to the tables
        Set tblInputs = wsInputs.ListObjects("Inputs")
        Set tblAnalysis = wsAnalysis.ListObjects("Analysis_T")
        Set tblSummaries = wsSummaries.ListObjects("Summaries")
        
        ' Loop through each row in the Inputs table
        For i = 1 To tblInputs.DataBodyRange.Rows.Count
            ' Get the current input row
            Set inputRow = tblInputs.ListRows(i)
            
            ' Clear the cells in Analysis_T
            tblAnalysis.DataBodyRange.ClearContents
            
            ' Copy the input row to the first row of Analysis_T
            inputRow.Range.Copy tblAnalysis.DataBodyRange.Rows(1)
            
            ' Wait for calculations to complete (if necessary)
            ' Add code here to wait for the calculations to complete
            
            ' Copy all rows from Analysis_T to Summaries
            lastRow = tblSummaries.DataBodyRange.Rows.Count
            tblAnalysis.DataBodyRange.Copy Destination:=tblSummaries.DataBodyRange.Offset(lastRow)
            
            ' Delete rows from Summaries where Result 2 is "No"
            DeleteRowsWithNoResult2 wsSummaries, tblSummaries
            
            ' Clear the clipboard
            Application.CutCopyMode = False
        Next i
    End Sub
    
    Sub DeleteRowsWithNoResult2(ws As Worksheet, tbl As ListObject)
        Dim result2Column As Range
        Dim cell As Range
        
        ' Set the Result 2 column range
        Set result2Column = tbl.ListColumns("Result 2").DataBodyRange
        
        ' Loop through each cell in the Result 2 column
        For Each cell In result2Column.Cells
            ' Check if the value is "No" and delete the entire row
            If cell.Value = "No" Then
                cell.EntireRow.Delete
            End If
        Next cell
    End Sub

     

    Please note that this is a basic example, and you may need to modify it based on your specific workbook structure and requirements. You'll also need to add code to wait for the calculations to complete, as indicated in the comments.

    To run the code, press Alt+F11 to open the VBA editor in Excel, insert a new module, and paste the code into the module. You can then run the IterateAndCopyPaste macro to perform the desired iterations.

     

    You can achieve the desired task using Power Query too in Excel.

    Here's a step-by-step solution using Power Query:

    1. Open your Excel workbook and go to the "Data" tab.
    2. Click on "Get Data" and select "From Other Sources" -> "Blank Query". This will open the Power Query Editor.
    3. In the Power Query Editor, go to the "Home" tab and click on "Advanced Editor".
    4. Replace the existing code with the following Power Query code:

     

    let
        Source = Excel.CurrentWorkbook(){[Name="Inputs"]}[Content],
        #"Filtered Rows" = Table.SelectRows(Source, each [#"Result 2"] = "Yes"),
        #"Removed Columns" = Table.RemoveColumns(#"Filtered Rows", {"Result 2"}),
        #"Renamed Columns" = Table.RenameColumns(#"Removed Columns",{{"Model", "Parts"}, {"Calculation 1", "Result 1"}, {"Calculation 2", "Result 2"}, {"Calculation 3", "Result 3"}}),
        #"Appended Queries" = Table.Combine({#"Renamed Columns", #"Renamed Columns", #"Renamed Columns"}) // Replace the number of repetitions here
    in
        #"Appended Queries"
    ​

     

    5. In the code above, replace "Inputs" with the actual name of your table in the "All Inputs" sheet.

    6. Replace the comment // Replace the number of repetitions here with the actual number of times you want to repeat the rows.

    7. Click on "Done" to close the Power Query Editor.

     

    This Power Query code will filter the rows based on "Result 2" column, remove the "Result 2" column, rename the columns accordingly, and append the resulting table multiple times based on the specified number of repetitions.

    Once you've completed the above steps, Power Query will load the transformed data into a new table in Excel. The table will contain the desired iterations based on your requirements.

    You can refresh the data in the table whenever needed by clicking on "Data" -> "Refresh All".

     

    I hope the proposal solutions, with AI helps, using VBA or Power Query meets your requirements.

9 Replies

  • NikolinoDE's avatar
    NikolinoDE
    Gold Contributor

    Antonino2023 

    You can started with the VBA code to achieve your task.

    Here's an example VBA code that you can use as a starting point:

     

    Sub IterateAndCopyPaste()
        Dim wb As Workbook
        Dim wsInputs As Worksheet
        Dim wsAnalysis As Worksheet
        Dim wsSummaries As Worksheet
        Dim tblInputs As ListObject
        Dim tblAnalysis As ListObject
        Dim tblSummaries As ListObject
        Dim inputRow As ListRow
        Dim analysisRow As ListRow
        Dim lastRow As Long
        Dim i As Long
        
        ' Set references to the workbook and worksheets
        Set wb = ThisWorkbook
        Set wsInputs = wb.Worksheets("All Inputs")
        Set wsAnalysis = wb.Worksheets("Analysis")
        Set wsSummaries = wb.Worksheets("Summaries")
        
        ' Set references to the tables
        Set tblInputs = wsInputs.ListObjects("Inputs")
        Set tblAnalysis = wsAnalysis.ListObjects("Analysis_T")
        Set tblSummaries = wsSummaries.ListObjects("Summaries")
        
        ' Loop through each row in the Inputs table
        For i = 1 To tblInputs.DataBodyRange.Rows.Count
            ' Get the current input row
            Set inputRow = tblInputs.ListRows(i)
            
            ' Clear the cells in Analysis_T
            tblAnalysis.DataBodyRange.ClearContents
            
            ' Copy the input row to the first row of Analysis_T
            inputRow.Range.Copy tblAnalysis.DataBodyRange.Rows(1)
            
            ' Wait for calculations to complete (if necessary)
            ' Add code here to wait for the calculations to complete
            
            ' Copy all rows from Analysis_T to Summaries
            lastRow = tblSummaries.DataBodyRange.Rows.Count
            tblAnalysis.DataBodyRange.Copy Destination:=tblSummaries.DataBodyRange.Offset(lastRow)
            
            ' Delete rows from Summaries where Result 2 is "No"
            DeleteRowsWithNoResult2 wsSummaries, tblSummaries
            
            ' Clear the clipboard
            Application.CutCopyMode = False
        Next i
    End Sub
    
    Sub DeleteRowsWithNoResult2(ws As Worksheet, tbl As ListObject)
        Dim result2Column As Range
        Dim cell As Range
        
        ' Set the Result 2 column range
        Set result2Column = tbl.ListColumns("Result 2").DataBodyRange
        
        ' Loop through each cell in the Result 2 column
        For Each cell In result2Column.Cells
            ' Check if the value is "No" and delete the entire row
            If cell.Value = "No" Then
                cell.EntireRow.Delete
            End If
        Next cell
    End Sub

     

    Please note that this is a basic example, and you may need to modify it based on your specific workbook structure and requirements. You'll also need to add code to wait for the calculations to complete, as indicated in the comments.

    To run the code, press Alt+F11 to open the VBA editor in Excel, insert a new module, and paste the code into the module. You can then run the IterateAndCopyPaste macro to perform the desired iterations.

     

    You can achieve the desired task using Power Query too in Excel.

    Here's a step-by-step solution using Power Query:

    1. Open your Excel workbook and go to the "Data" tab.
    2. Click on "Get Data" and select "From Other Sources" -> "Blank Query". This will open the Power Query Editor.
    3. In the Power Query Editor, go to the "Home" tab and click on "Advanced Editor".
    4. Replace the existing code with the following Power Query code:

     

    let
        Source = Excel.CurrentWorkbook(){[Name="Inputs"]}[Content],
        #"Filtered Rows" = Table.SelectRows(Source, each [#"Result 2"] = "Yes"),
        #"Removed Columns" = Table.RemoveColumns(#"Filtered Rows", {"Result 2"}),
        #"Renamed Columns" = Table.RenameColumns(#"Removed Columns",{{"Model", "Parts"}, {"Calculation 1", "Result 1"}, {"Calculation 2", "Result 2"}, {"Calculation 3", "Result 3"}}),
        #"Appended Queries" = Table.Combine({#"Renamed Columns", #"Renamed Columns", #"Renamed Columns"}) // Replace the number of repetitions here
    in
        #"Appended Queries"
    ​

     

    5. In the code above, replace "Inputs" with the actual name of your table in the "All Inputs" sheet.

    6. Replace the comment // Replace the number of repetitions here with the actual number of times you want to repeat the rows.

    7. Click on "Done" to close the Power Query Editor.

     

    This Power Query code will filter the rows based on "Result 2" column, remove the "Result 2" column, rename the columns accordingly, and append the resulting table multiple times based on the specified number of repetitions.

    Once you've completed the above steps, Power Query will load the transformed data into a new table in Excel. The table will contain the desired iterations based on your requirements.

    You can refresh the data in the table whenever needed by clicking on "Data" -> "Refresh All".

     

    I hope the proposal solutions, with AI helps, using VBA or Power Query meets your requirements.

    • Antonino2023's avatar
      Antonino2023
      Brass Contributor

      NikolinoDE 

       

      I have made some modifications to your template code to fit my needs. I am now at a point where I would like to paste as values instead of  the traditional copy./paste. Do you have any suggestions for doing that? I suspect it will require modifying line 64. I have attached a copy of the xlsm file to this post.

       

      Also, do you have any suggestions for improving the speed of this code? 

       

      Sub IterateAndCopyPaste_V2()
          Dim wb As Workbook
          Dim wsInputs As Worksheet
          Dim wsAnalysis As Worksheet
          Dim wsSummaries As Worksheet
          Dim tblInputs As ListObject
          Dim tblModel As ListObject
          Dim tblAnalysis As ListObject
          Dim tblSummaries As ListObject
          Dim inputRow As ListRow
          Dim analysisRow As ListRow
          Dim lastRow As Long
          Dim i As Long
          
          ' Temporary ovewrite value
          Dim Temp As String
          Temp = "Temp"
          
          ' Set references to the workbook and worksheets
          Set wb = ThisWorkbook
          Set wsInputs = wb.Worksheets("Inputs")
          Set wsAnalysis = wb.Worksheets("Analysis")
          Set wsSummaries = wb.Worksheets("Summaries")
          
          ' Set references to the tables
          Set tblInputs = wsInputs.ListObjects("Inputs_T")
          Set tblModel = wsAnalysis.ListObjects("Model_T")
          Set tblAnalysis = wsAnalysis.ListObjects("Analysis_T")
          Set tblSummaries = wsSummaries.ListObjects("Summaries_T")
          
          ' Helper variables
          num_Inputs_rows = tblInputs.DataBodyRange.Rows.Count
          On Error Resume Next    ' This is a way to handle the error when SpecialCells(xcellTypeBlanks) is empty and returns an error
          num_Inputs_empty_rows = tblInputs.DataBodyRange.SpecialCells(xlCellTypeBlanks).Rows.Count
          On Error GoTo 0
          data_num_Inputs_rows = num_Inputs_rows - num_Inputs_empty_rows
          
          ' Clear tblSummaries before beginning first/new iteration
          tblSummaries.Range(2, 1).Value = Temp   ' This is a temporary fix to allow table to clear in next line
          tblSummaries.DataBodyRange.ClearContents
          tblSummaries.DataBodyRange.Delete
          tblSummaries.Range(2, 1).Value = Temp   ' Temp is added back in to allow the For loop to identify LastRow for i = 1 (throws an error without this)
          
          ' Loop through each row in tblInputs
          For i = 1 To data_num_Inputs_rows
              
              ' Get the current input row
              Set inputRow = tblInputs.ListRows(i)
              
              ' Clear the cells in Model_T
              tblModel.DataBodyRange.ClearContents    ' This prevents a pop-up on every iteration asking to replace cells with existing data
                      
              ' Copy the input row to the first row of Model_T
              inputRow.Range.Copy tblModel.DataBodyRange.Rows(1)
              
              ' Wait for calculations to complete (if necessary)
              ' (In testing this is a buffer to observe impact of code modifications)
              Application.Wait (Now + TimeValue("0:00:05"))
              
              ' Determine which row the Analysis data should be pasted in based on current iteration (i)
              lastRow = tblSummaries.ListRows.Count
              
              ' Copy all rows from tblAnalysis to tblSummaries
              tblAnalysis.DataBodyRange.Copy Destination:=tblSummaries.DataBodyRange.Offset(lastRow - 1)
                      
              If i = data_num_Inputs_rows Then
              tblModel.DataBodyRange.ClearContents ' Clear tblModel
              MsgBox "Analysis complete. Go to the Summaries sheet and make an offline copy."
              End If
                      
              ' Clear the clipboard
              Application.CutCopyMode = False
           Next i
      End Sub
      
      'Sub DeleteBlankRows_V2(ws As Worksheet, tbl As ListObject)
      '    Dim AllSummaries As Range
      '    Set AllSummaries = tbl.DataBodyRange
      '    AllSummaries.SpecialCells(xlCellTypeBlanks).EntireRow.Delete
      'End Sub

       

    • Antonino2023's avatar
      Antonino2023
      Brass Contributor
      NikolinoDE

      Thank you for your detailed response. I will implement your suggestions and get back to you with any questions/issues that may arise!

      Regards,

Resources