Macro for copying data from tabs based on filtered line












1















I would like to have a macro which copies all data from several tabs, which are in line with filter line defined in "Summary (Filtered)" tab. Here are the details:




  1. All tabs have the same headers.

  2. The filter line is row 7 in tab "Summary (Filtered)".

  3. I want to loop through every tab except for those listed below, check every row and copy it to Summary tab if it satisfies the filter (if given cell in filter line is empty, all values are permitted, otherwise it must match).

  4. I would like the copying to start in line 9 of Summary tab.


I have tried to solve it by the loop functions, but I get application or object defined error. Also, I imagine that the effectiveness of double loops is very poor.



Sub CopyDataFiltered()
Dim sh As Worksheet
Dim SourceSh As Worksheet
Dim Last As Long
Dim shLast As Long
Dim CopyRng As Range
Dim StartRow As Long
Dim lrow As Long
Dim r As Long
Dim col As Long

With Application
.ScreenUpdating = False
.EnableEvents = False
End With

Set SourceSh = ActiveWorkbook.Worksheets("Summary (Filtered)")
Application.DisplayAlerts = False
On Error Resume Next
On Error GoTo 0

For Each sh In ActiveWorkbook.Worksheets
If IsError(Application.Match(sh.Name, Array(SourceSh.Name, "List Data", "Summary (All)", "Lists"), 0)) Then

lrow = LastRow(sh)

If lrow < 7 Then
'MsgBox ("Nothing to move")
GoTo NextTab
End If

For r = LastRow(sh) To 7 Step -1
For col = 1 To 16

If SourceSh.Range(7, col).Value <> "" And SourceSh.Range(7, col).Value <> sh.Range(r, col).Value Then
GoTo End1
End If

Next col
sh.Rows(r).Copy Destination:=SourceSh.Range("A" & LastRow(SourceSh) + 1)

End1:
Next r
End If
NextTab:
Next

ExitTheSub:
Application.Goto SourceSh.Cells(1)
Application.DisplayAlerts = True
With Application
.ScreenUpdating = True
.EnableEvents = True
End With
End Sub


Could you please take a look and let me know what you think would be the best?










share|improve this question





























    1















    I would like to have a macro which copies all data from several tabs, which are in line with filter line defined in "Summary (Filtered)" tab. Here are the details:




    1. All tabs have the same headers.

    2. The filter line is row 7 in tab "Summary (Filtered)".

    3. I want to loop through every tab except for those listed below, check every row and copy it to Summary tab if it satisfies the filter (if given cell in filter line is empty, all values are permitted, otherwise it must match).

    4. I would like the copying to start in line 9 of Summary tab.


    I have tried to solve it by the loop functions, but I get application or object defined error. Also, I imagine that the effectiveness of double loops is very poor.



    Sub CopyDataFiltered()
    Dim sh As Worksheet
    Dim SourceSh As Worksheet
    Dim Last As Long
    Dim shLast As Long
    Dim CopyRng As Range
    Dim StartRow As Long
    Dim lrow As Long
    Dim r As Long
    Dim col As Long

    With Application
    .ScreenUpdating = False
    .EnableEvents = False
    End With

    Set SourceSh = ActiveWorkbook.Worksheets("Summary (Filtered)")
    Application.DisplayAlerts = False
    On Error Resume Next
    On Error GoTo 0

    For Each sh In ActiveWorkbook.Worksheets
    If IsError(Application.Match(sh.Name, Array(SourceSh.Name, "List Data", "Summary (All)", "Lists"), 0)) Then

    lrow = LastRow(sh)

    If lrow < 7 Then
    'MsgBox ("Nothing to move")
    GoTo NextTab
    End If

    For r = LastRow(sh) To 7 Step -1
    For col = 1 To 16

    If SourceSh.Range(7, col).Value <> "" And SourceSh.Range(7, col).Value <> sh.Range(r, col).Value Then
    GoTo End1
    End If

    Next col
    sh.Rows(r).Copy Destination:=SourceSh.Range("A" & LastRow(SourceSh) + 1)

    End1:
    Next r
    End If
    NextTab:
    Next

    ExitTheSub:
    Application.Goto SourceSh.Cells(1)
    Application.DisplayAlerts = True
    With Application
    .ScreenUpdating = True
    .EnableEvents = True
    End With
    End Sub


    Could you please take a look and let me know what you think would be the best?










    share|improve this question



























      1












      1








      1








      I would like to have a macro which copies all data from several tabs, which are in line with filter line defined in "Summary (Filtered)" tab. Here are the details:




      1. All tabs have the same headers.

      2. The filter line is row 7 in tab "Summary (Filtered)".

      3. I want to loop through every tab except for those listed below, check every row and copy it to Summary tab if it satisfies the filter (if given cell in filter line is empty, all values are permitted, otherwise it must match).

      4. I would like the copying to start in line 9 of Summary tab.


      I have tried to solve it by the loop functions, but I get application or object defined error. Also, I imagine that the effectiveness of double loops is very poor.



      Sub CopyDataFiltered()
      Dim sh As Worksheet
      Dim SourceSh As Worksheet
      Dim Last As Long
      Dim shLast As Long
      Dim CopyRng As Range
      Dim StartRow As Long
      Dim lrow As Long
      Dim r As Long
      Dim col As Long

      With Application
      .ScreenUpdating = False
      .EnableEvents = False
      End With

      Set SourceSh = ActiveWorkbook.Worksheets("Summary (Filtered)")
      Application.DisplayAlerts = False
      On Error Resume Next
      On Error GoTo 0

      For Each sh In ActiveWorkbook.Worksheets
      If IsError(Application.Match(sh.Name, Array(SourceSh.Name, "List Data", "Summary (All)", "Lists"), 0)) Then

      lrow = LastRow(sh)

      If lrow < 7 Then
      'MsgBox ("Nothing to move")
      GoTo NextTab
      End If

      For r = LastRow(sh) To 7 Step -1
      For col = 1 To 16

      If SourceSh.Range(7, col).Value <> "" And SourceSh.Range(7, col).Value <> sh.Range(r, col).Value Then
      GoTo End1
      End If

      Next col
      sh.Rows(r).Copy Destination:=SourceSh.Range("A" & LastRow(SourceSh) + 1)

      End1:
      Next r
      End If
      NextTab:
      Next

      ExitTheSub:
      Application.Goto SourceSh.Cells(1)
      Application.DisplayAlerts = True
      With Application
      .ScreenUpdating = True
      .EnableEvents = True
      End With
      End Sub


      Could you please take a look and let me know what you think would be the best?










      share|improve this question
















      I would like to have a macro which copies all data from several tabs, which are in line with filter line defined in "Summary (Filtered)" tab. Here are the details:




      1. All tabs have the same headers.

      2. The filter line is row 7 in tab "Summary (Filtered)".

      3. I want to loop through every tab except for those listed below, check every row and copy it to Summary tab if it satisfies the filter (if given cell in filter line is empty, all values are permitted, otherwise it must match).

      4. I would like the copying to start in line 9 of Summary tab.


      I have tried to solve it by the loop functions, but I get application or object defined error. Also, I imagine that the effectiveness of double loops is very poor.



      Sub CopyDataFiltered()
      Dim sh As Worksheet
      Dim SourceSh As Worksheet
      Dim Last As Long
      Dim shLast As Long
      Dim CopyRng As Range
      Dim StartRow As Long
      Dim lrow As Long
      Dim r As Long
      Dim col As Long

      With Application
      .ScreenUpdating = False
      .EnableEvents = False
      End With

      Set SourceSh = ActiveWorkbook.Worksheets("Summary (Filtered)")
      Application.DisplayAlerts = False
      On Error Resume Next
      On Error GoTo 0

      For Each sh In ActiveWorkbook.Worksheets
      If IsError(Application.Match(sh.Name, Array(SourceSh.Name, "List Data", "Summary (All)", "Lists"), 0)) Then

      lrow = LastRow(sh)

      If lrow < 7 Then
      'MsgBox ("Nothing to move")
      GoTo NextTab
      End If

      For r = LastRow(sh) To 7 Step -1
      For col = 1 To 16

      If SourceSh.Range(7, col).Value <> "" And SourceSh.Range(7, col).Value <> sh.Range(r, col).Value Then
      GoTo End1
      End If

      Next col
      sh.Rows(r).Copy Destination:=SourceSh.Range("A" & LastRow(SourceSh) + 1)

      End1:
      Next r
      End If
      NextTab:
      Next

      ExitTheSub:
      Application.Goto SourceSh.Cells(1)
      Application.DisplayAlerts = True
      With Application
      .ScreenUpdating = True
      .EnableEvents = True
      End With
      End Sub


      Could you please take a look and let me know what you think would be the best?







      excel vba excel-vba






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited Jan 2 at 14:09









      J.schmidt

      656120




      656120










      asked Jan 2 at 13:25









      JohnJohn

      235




      235
























          1 Answer
          1






          active

          oldest

          votes


















          2














          So here is almost your same approach, just reworked into a form that isolates each step of your process clarify what you want to accomplish. Having a nested loop is not a problem, as long as you keep track of what you're trying to do. What I do want to steer you away from is using GoTo statements. They are almost never necessary.



          So first things first...



          Always use Option Explicit and declare your variables as close as possible to where you want to use them. This habit makes it easier to understand what each variable is and what it's used for. If you declare them all at the top, you'll always be popping back and forth to find them.



          Option Explicit

          Sub CopyFilteredData()
          Dim srcWB As Workbook
          Dim srcWS As Worksheet
          Set srcWB = ActiveWorkbook
          Set srcWS = srcWB.Sheets("Summary (Filtered)")


          Since you will always be referring to your filter in the same location, just define a variable that specifically matches your filter. The bonus here is if your filter changes from row 7 to row 8 (for example), you only have to change it in one spot.



              Dim srcFilter As Range
          Set srcFilter = srcWS.Range("A7").Resize(1, 16)


          Using the same idea, set up a variable that clearly defines the worksheets to skip:



              Dim skipTheseSheets As Variant
          skipTheseSheets = Array(srcWS.Name, "List Data", "Summary (All)", "Lists")
          Dim sh As Worksheet
          For Each sh In srcWB.Sheets
          If Not IsInArray(sh.Name, skipTheseSheets) Then


          This answer gives an excellent function to check if your worksheet name exists in that array.



          You didn't include your function for LastRow, so I included it in my answer. However, make a habit of naming your functions using a verb that is descriptive of what the function does. In this case FindLastRow.



          In order to stop using GoTo statements, just reverse the If statement and proceed if it passes:



          Dim lastRow As Long
          lastRow = FindLastRow(sh)
          If lastRow > 7 Then


          I created a separate function that compares a given row against your filter. It uses basically your same logic, but by isolating it as a function, it makes your main logic read more simply. Also, notice that you can exit a For loop and avoid the dreaded GoTo:



          Private Function RowMatchesFilter(ByRef thisRow As Range, _
          ByRef thisFilter As Range) As Boolean
          '--- the row matches only if the value in thisRow equals the value
          ' in the filter
          RowMatchesFilter = True
          Dim i As Long
          For i = 1 To 16
          If Not IsEmpty(thisFilter.Cells(1, i).Value) Then
          If thisRow.Cells(1, i).Value <> thisFilter.Cells(1, i).Value Then
          '--- the first cell that doesn't match invalidates the
          ' entire row
          RowMatchesFilter = False
          Exit For
          End If
          End If
          Next i
          End Function


          So your copy loop ends up looking like this:



          Dim r As Long
          For r = lastRow To 7 Step -1
          If RowMatchesFilter(sh.Rows(r), srcFilter) Then
          sh.Rows(r).Copy
          srcWS.Range("A" & FindLastRow(srcWS) + 1).PasteSpecial xlPasteAll
          End If
          Next r


          Here is the whole module:



          Option Explicit

          Sub CopyFilteredData()
          Dim srcWB As Workbook
          Dim srcWS As Worksheet
          Set srcWB = ActiveWorkbook
          Set srcWS = srcWB.Sheets("Summary (Filtered)")

          Dim srcFilter As Range
          Set srcFilter = srcWS.Range("A7").Resize(1, 16)

          Dim skipTheseSheets As Variant
          skipTheseSheets = Array(srcWS.Name, "List Data", "Summary (All)", "Lists")

          Dim sh As Worksheet
          For Each sh In srcWB.Sheets
          If Not IsInArray(sh.Name, skipTheseSheets) Then
          Dim lastRow As Long
          lastRow = FindLastRow(sh)
          If lastRow > 7 Then
          '--- now copy the data from this sheet back to the source
          ' in reverse order, using the source filter line to
          ' direct which cells to copy
          Dim r As Long
          For r = lastRow To 7 Step -1
          If RowMatchesFilter(sh.Rows(r), srcFilter) Then
          sh.Rows(r).Copy
          srcWS.Range("A" & FindLastRow(srcWS) + 1).PasteSpecial xlPasteAll
          End If
          Next r
          End If
          End If
          Next sh
          End Sub

          Private Function IsInArray(ByVal stringToBeFound As String, _
          ByRef arr As Variant) As Boolean
          IsInArray = (UBound(Filter(arr, stringToBeFound)) > -1)
          End Function

          Private Function FindLastRow(ByRef thisWS As Worksheet) As Long
          With thisWS
          FindLastRow = .Cells(.Cells.Rows.Count, 1).End(xlUp).Row
          End With
          End Function

          Private Function RowMatchesFilter(ByRef thisRow As Range, _
          ByRef thisFilter As Range) As Boolean
          '--- the row matches only if the value in thisRow equals the value
          ' in the filter
          RowMatchesFilter = True
          Dim i As Long
          For i = 1 To 16
          If Not IsEmpty(thisFilter.Cells(1, i).Value) Then
          If thisRow.Cells(1, i).Value <> thisFilter.Cells(1, i).Value Then
          '--- the first cell that doesn't match invalidates the
          ' entire row
          RowMatchesFilter = False
          Exit For
          End If
          End If
          Next i
          End Function





          share|improve this answer
























          • That's brilliant, man - works like a dram, thank you! I don't have any additional questions. The only issue was with FindLastRow function but I solved it. Great work!

            – John
            Jan 2 at 15:35













          • Hello, I would like to reopen the thread as there is one problem which I didn't anticipate. As of now, the macro has problem when in source tabs, the different values appear based on formula. Namely it is either "Check" or blank (""). Once in filter line there is only those with Check in source tab, the macro doesn't return correct data (also brings up blanks, but not all of them) I would really appreciate your help on that one.

            – John
            Jan 23 at 13:39











          • I'm a little unclear what you're asking, and since it seems like a separate question it's best to go ahead and create a new question. You can link back to this post if you like, but still share example data and code so folks here can have the best chance to get you a quick answer.

            – PeterT
            Jan 23 at 17:43











          • I've created another question with the reference to this one as the problem may be considered as different. You may find it here: stackoverflow.com/questions/54365213/…

            – John
            Jan 25 at 12:19













          Your Answer






          StackExchange.ifUsing("editor", function () {
          StackExchange.using("externalEditor", function () {
          StackExchange.using("snippets", function () {
          StackExchange.snippets.init();
          });
          });
          }, "code-snippets");

          StackExchange.ready(function() {
          var channelOptions = {
          tags: "".split(" "),
          id: "1"
          };
          initTagRenderer("".split(" "), "".split(" "), channelOptions);

          StackExchange.using("externalEditor", function() {
          // Have to fire editor after snippets, if snippets enabled
          if (StackExchange.settings.snippets.snippetsEnabled) {
          StackExchange.using("snippets", function() {
          createEditor();
          });
          }
          else {
          createEditor();
          }
          });

          function createEditor() {
          StackExchange.prepareEditor({
          heartbeatType: 'answer',
          autoActivateHeartbeat: false,
          convertImagesToLinks: true,
          noModals: true,
          showLowRepImageUploadWarning: true,
          reputationToPostImages: 10,
          bindNavPrevention: true,
          postfix: "",
          imageUploader: {
          brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
          contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
          allowUrls: true
          },
          onDemand: true,
          discardSelector: ".discard-answer"
          ,immediatelyShowMarkdownHelp:true
          });


          }
          });














          draft saved

          draft discarded


















          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54007190%2fmacro-for-copying-data-from-tabs-based-on-filtered-line%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown

























          1 Answer
          1






          active

          oldest

          votes








          1 Answer
          1






          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes









          2














          So here is almost your same approach, just reworked into a form that isolates each step of your process clarify what you want to accomplish. Having a nested loop is not a problem, as long as you keep track of what you're trying to do. What I do want to steer you away from is using GoTo statements. They are almost never necessary.



          So first things first...



          Always use Option Explicit and declare your variables as close as possible to where you want to use them. This habit makes it easier to understand what each variable is and what it's used for. If you declare them all at the top, you'll always be popping back and forth to find them.



          Option Explicit

          Sub CopyFilteredData()
          Dim srcWB As Workbook
          Dim srcWS As Worksheet
          Set srcWB = ActiveWorkbook
          Set srcWS = srcWB.Sheets("Summary (Filtered)")


          Since you will always be referring to your filter in the same location, just define a variable that specifically matches your filter. The bonus here is if your filter changes from row 7 to row 8 (for example), you only have to change it in one spot.



              Dim srcFilter As Range
          Set srcFilter = srcWS.Range("A7").Resize(1, 16)


          Using the same idea, set up a variable that clearly defines the worksheets to skip:



              Dim skipTheseSheets As Variant
          skipTheseSheets = Array(srcWS.Name, "List Data", "Summary (All)", "Lists")
          Dim sh As Worksheet
          For Each sh In srcWB.Sheets
          If Not IsInArray(sh.Name, skipTheseSheets) Then


          This answer gives an excellent function to check if your worksheet name exists in that array.



          You didn't include your function for LastRow, so I included it in my answer. However, make a habit of naming your functions using a verb that is descriptive of what the function does. In this case FindLastRow.



          In order to stop using GoTo statements, just reverse the If statement and proceed if it passes:



          Dim lastRow As Long
          lastRow = FindLastRow(sh)
          If lastRow > 7 Then


          I created a separate function that compares a given row against your filter. It uses basically your same logic, but by isolating it as a function, it makes your main logic read more simply. Also, notice that you can exit a For loop and avoid the dreaded GoTo:



          Private Function RowMatchesFilter(ByRef thisRow As Range, _
          ByRef thisFilter As Range) As Boolean
          '--- the row matches only if the value in thisRow equals the value
          ' in the filter
          RowMatchesFilter = True
          Dim i As Long
          For i = 1 To 16
          If Not IsEmpty(thisFilter.Cells(1, i).Value) Then
          If thisRow.Cells(1, i).Value <> thisFilter.Cells(1, i).Value Then
          '--- the first cell that doesn't match invalidates the
          ' entire row
          RowMatchesFilter = False
          Exit For
          End If
          End If
          Next i
          End Function


          So your copy loop ends up looking like this:



          Dim r As Long
          For r = lastRow To 7 Step -1
          If RowMatchesFilter(sh.Rows(r), srcFilter) Then
          sh.Rows(r).Copy
          srcWS.Range("A" & FindLastRow(srcWS) + 1).PasteSpecial xlPasteAll
          End If
          Next r


          Here is the whole module:



          Option Explicit

          Sub CopyFilteredData()
          Dim srcWB As Workbook
          Dim srcWS As Worksheet
          Set srcWB = ActiveWorkbook
          Set srcWS = srcWB.Sheets("Summary (Filtered)")

          Dim srcFilter As Range
          Set srcFilter = srcWS.Range("A7").Resize(1, 16)

          Dim skipTheseSheets As Variant
          skipTheseSheets = Array(srcWS.Name, "List Data", "Summary (All)", "Lists")

          Dim sh As Worksheet
          For Each sh In srcWB.Sheets
          If Not IsInArray(sh.Name, skipTheseSheets) Then
          Dim lastRow As Long
          lastRow = FindLastRow(sh)
          If lastRow > 7 Then
          '--- now copy the data from this sheet back to the source
          ' in reverse order, using the source filter line to
          ' direct which cells to copy
          Dim r As Long
          For r = lastRow To 7 Step -1
          If RowMatchesFilter(sh.Rows(r), srcFilter) Then
          sh.Rows(r).Copy
          srcWS.Range("A" & FindLastRow(srcWS) + 1).PasteSpecial xlPasteAll
          End If
          Next r
          End If
          End If
          Next sh
          End Sub

          Private Function IsInArray(ByVal stringToBeFound As String, _
          ByRef arr As Variant) As Boolean
          IsInArray = (UBound(Filter(arr, stringToBeFound)) > -1)
          End Function

          Private Function FindLastRow(ByRef thisWS As Worksheet) As Long
          With thisWS
          FindLastRow = .Cells(.Cells.Rows.Count, 1).End(xlUp).Row
          End With
          End Function

          Private Function RowMatchesFilter(ByRef thisRow As Range, _
          ByRef thisFilter As Range) As Boolean
          '--- the row matches only if the value in thisRow equals the value
          ' in the filter
          RowMatchesFilter = True
          Dim i As Long
          For i = 1 To 16
          If Not IsEmpty(thisFilter.Cells(1, i).Value) Then
          If thisRow.Cells(1, i).Value <> thisFilter.Cells(1, i).Value Then
          '--- the first cell that doesn't match invalidates the
          ' entire row
          RowMatchesFilter = False
          Exit For
          End If
          End If
          Next i
          End Function





          share|improve this answer
























          • That's brilliant, man - works like a dram, thank you! I don't have any additional questions. The only issue was with FindLastRow function but I solved it. Great work!

            – John
            Jan 2 at 15:35













          • Hello, I would like to reopen the thread as there is one problem which I didn't anticipate. As of now, the macro has problem when in source tabs, the different values appear based on formula. Namely it is either "Check" or blank (""). Once in filter line there is only those with Check in source tab, the macro doesn't return correct data (also brings up blanks, but not all of them) I would really appreciate your help on that one.

            – John
            Jan 23 at 13:39











          • I'm a little unclear what you're asking, and since it seems like a separate question it's best to go ahead and create a new question. You can link back to this post if you like, but still share example data and code so folks here can have the best chance to get you a quick answer.

            – PeterT
            Jan 23 at 17:43











          • I've created another question with the reference to this one as the problem may be considered as different. You may find it here: stackoverflow.com/questions/54365213/…

            – John
            Jan 25 at 12:19


















          2














          So here is almost your same approach, just reworked into a form that isolates each step of your process clarify what you want to accomplish. Having a nested loop is not a problem, as long as you keep track of what you're trying to do. What I do want to steer you away from is using GoTo statements. They are almost never necessary.



          So first things first...



          Always use Option Explicit and declare your variables as close as possible to where you want to use them. This habit makes it easier to understand what each variable is and what it's used for. If you declare them all at the top, you'll always be popping back and forth to find them.



          Option Explicit

          Sub CopyFilteredData()
          Dim srcWB As Workbook
          Dim srcWS As Worksheet
          Set srcWB = ActiveWorkbook
          Set srcWS = srcWB.Sheets("Summary (Filtered)")


          Since you will always be referring to your filter in the same location, just define a variable that specifically matches your filter. The bonus here is if your filter changes from row 7 to row 8 (for example), you only have to change it in one spot.



              Dim srcFilter As Range
          Set srcFilter = srcWS.Range("A7").Resize(1, 16)


          Using the same idea, set up a variable that clearly defines the worksheets to skip:



              Dim skipTheseSheets As Variant
          skipTheseSheets = Array(srcWS.Name, "List Data", "Summary (All)", "Lists")
          Dim sh As Worksheet
          For Each sh In srcWB.Sheets
          If Not IsInArray(sh.Name, skipTheseSheets) Then


          This answer gives an excellent function to check if your worksheet name exists in that array.



          You didn't include your function for LastRow, so I included it in my answer. However, make a habit of naming your functions using a verb that is descriptive of what the function does. In this case FindLastRow.



          In order to stop using GoTo statements, just reverse the If statement and proceed if it passes:



          Dim lastRow As Long
          lastRow = FindLastRow(sh)
          If lastRow > 7 Then


          I created a separate function that compares a given row against your filter. It uses basically your same logic, but by isolating it as a function, it makes your main logic read more simply. Also, notice that you can exit a For loop and avoid the dreaded GoTo:



          Private Function RowMatchesFilter(ByRef thisRow As Range, _
          ByRef thisFilter As Range) As Boolean
          '--- the row matches only if the value in thisRow equals the value
          ' in the filter
          RowMatchesFilter = True
          Dim i As Long
          For i = 1 To 16
          If Not IsEmpty(thisFilter.Cells(1, i).Value) Then
          If thisRow.Cells(1, i).Value <> thisFilter.Cells(1, i).Value Then
          '--- the first cell that doesn't match invalidates the
          ' entire row
          RowMatchesFilter = False
          Exit For
          End If
          End If
          Next i
          End Function


          So your copy loop ends up looking like this:



          Dim r As Long
          For r = lastRow To 7 Step -1
          If RowMatchesFilter(sh.Rows(r), srcFilter) Then
          sh.Rows(r).Copy
          srcWS.Range("A" & FindLastRow(srcWS) + 1).PasteSpecial xlPasteAll
          End If
          Next r


          Here is the whole module:



          Option Explicit

          Sub CopyFilteredData()
          Dim srcWB As Workbook
          Dim srcWS As Worksheet
          Set srcWB = ActiveWorkbook
          Set srcWS = srcWB.Sheets("Summary (Filtered)")

          Dim srcFilter As Range
          Set srcFilter = srcWS.Range("A7").Resize(1, 16)

          Dim skipTheseSheets As Variant
          skipTheseSheets = Array(srcWS.Name, "List Data", "Summary (All)", "Lists")

          Dim sh As Worksheet
          For Each sh In srcWB.Sheets
          If Not IsInArray(sh.Name, skipTheseSheets) Then
          Dim lastRow As Long
          lastRow = FindLastRow(sh)
          If lastRow > 7 Then
          '--- now copy the data from this sheet back to the source
          ' in reverse order, using the source filter line to
          ' direct which cells to copy
          Dim r As Long
          For r = lastRow To 7 Step -1
          If RowMatchesFilter(sh.Rows(r), srcFilter) Then
          sh.Rows(r).Copy
          srcWS.Range("A" & FindLastRow(srcWS) + 1).PasteSpecial xlPasteAll
          End If
          Next r
          End If
          End If
          Next sh
          End Sub

          Private Function IsInArray(ByVal stringToBeFound As String, _
          ByRef arr As Variant) As Boolean
          IsInArray = (UBound(Filter(arr, stringToBeFound)) > -1)
          End Function

          Private Function FindLastRow(ByRef thisWS As Worksheet) As Long
          With thisWS
          FindLastRow = .Cells(.Cells.Rows.Count, 1).End(xlUp).Row
          End With
          End Function

          Private Function RowMatchesFilter(ByRef thisRow As Range, _
          ByRef thisFilter As Range) As Boolean
          '--- the row matches only if the value in thisRow equals the value
          ' in the filter
          RowMatchesFilter = True
          Dim i As Long
          For i = 1 To 16
          If Not IsEmpty(thisFilter.Cells(1, i).Value) Then
          If thisRow.Cells(1, i).Value <> thisFilter.Cells(1, i).Value Then
          '--- the first cell that doesn't match invalidates the
          ' entire row
          RowMatchesFilter = False
          Exit For
          End If
          End If
          Next i
          End Function





          share|improve this answer
























          • That's brilliant, man - works like a dram, thank you! I don't have any additional questions. The only issue was with FindLastRow function but I solved it. Great work!

            – John
            Jan 2 at 15:35













          • Hello, I would like to reopen the thread as there is one problem which I didn't anticipate. As of now, the macro has problem when in source tabs, the different values appear based on formula. Namely it is either "Check" or blank (""). Once in filter line there is only those with Check in source tab, the macro doesn't return correct data (also brings up blanks, but not all of them) I would really appreciate your help on that one.

            – John
            Jan 23 at 13:39











          • I'm a little unclear what you're asking, and since it seems like a separate question it's best to go ahead and create a new question. You can link back to this post if you like, but still share example data and code so folks here can have the best chance to get you a quick answer.

            – PeterT
            Jan 23 at 17:43











          • I've created another question with the reference to this one as the problem may be considered as different. You may find it here: stackoverflow.com/questions/54365213/…

            – John
            Jan 25 at 12:19
















          2












          2








          2







          So here is almost your same approach, just reworked into a form that isolates each step of your process clarify what you want to accomplish. Having a nested loop is not a problem, as long as you keep track of what you're trying to do. What I do want to steer you away from is using GoTo statements. They are almost never necessary.



          So first things first...



          Always use Option Explicit and declare your variables as close as possible to where you want to use them. This habit makes it easier to understand what each variable is and what it's used for. If you declare them all at the top, you'll always be popping back and forth to find them.



          Option Explicit

          Sub CopyFilteredData()
          Dim srcWB As Workbook
          Dim srcWS As Worksheet
          Set srcWB = ActiveWorkbook
          Set srcWS = srcWB.Sheets("Summary (Filtered)")


          Since you will always be referring to your filter in the same location, just define a variable that specifically matches your filter. The bonus here is if your filter changes from row 7 to row 8 (for example), you only have to change it in one spot.



              Dim srcFilter As Range
          Set srcFilter = srcWS.Range("A7").Resize(1, 16)


          Using the same idea, set up a variable that clearly defines the worksheets to skip:



              Dim skipTheseSheets As Variant
          skipTheseSheets = Array(srcWS.Name, "List Data", "Summary (All)", "Lists")
          Dim sh As Worksheet
          For Each sh In srcWB.Sheets
          If Not IsInArray(sh.Name, skipTheseSheets) Then


          This answer gives an excellent function to check if your worksheet name exists in that array.



          You didn't include your function for LastRow, so I included it in my answer. However, make a habit of naming your functions using a verb that is descriptive of what the function does. In this case FindLastRow.



          In order to stop using GoTo statements, just reverse the If statement and proceed if it passes:



          Dim lastRow As Long
          lastRow = FindLastRow(sh)
          If lastRow > 7 Then


          I created a separate function that compares a given row against your filter. It uses basically your same logic, but by isolating it as a function, it makes your main logic read more simply. Also, notice that you can exit a For loop and avoid the dreaded GoTo:



          Private Function RowMatchesFilter(ByRef thisRow As Range, _
          ByRef thisFilter As Range) As Boolean
          '--- the row matches only if the value in thisRow equals the value
          ' in the filter
          RowMatchesFilter = True
          Dim i As Long
          For i = 1 To 16
          If Not IsEmpty(thisFilter.Cells(1, i).Value) Then
          If thisRow.Cells(1, i).Value <> thisFilter.Cells(1, i).Value Then
          '--- the first cell that doesn't match invalidates the
          ' entire row
          RowMatchesFilter = False
          Exit For
          End If
          End If
          Next i
          End Function


          So your copy loop ends up looking like this:



          Dim r As Long
          For r = lastRow To 7 Step -1
          If RowMatchesFilter(sh.Rows(r), srcFilter) Then
          sh.Rows(r).Copy
          srcWS.Range("A" & FindLastRow(srcWS) + 1).PasteSpecial xlPasteAll
          End If
          Next r


          Here is the whole module:



          Option Explicit

          Sub CopyFilteredData()
          Dim srcWB As Workbook
          Dim srcWS As Worksheet
          Set srcWB = ActiveWorkbook
          Set srcWS = srcWB.Sheets("Summary (Filtered)")

          Dim srcFilter As Range
          Set srcFilter = srcWS.Range("A7").Resize(1, 16)

          Dim skipTheseSheets As Variant
          skipTheseSheets = Array(srcWS.Name, "List Data", "Summary (All)", "Lists")

          Dim sh As Worksheet
          For Each sh In srcWB.Sheets
          If Not IsInArray(sh.Name, skipTheseSheets) Then
          Dim lastRow As Long
          lastRow = FindLastRow(sh)
          If lastRow > 7 Then
          '--- now copy the data from this sheet back to the source
          ' in reverse order, using the source filter line to
          ' direct which cells to copy
          Dim r As Long
          For r = lastRow To 7 Step -1
          If RowMatchesFilter(sh.Rows(r), srcFilter) Then
          sh.Rows(r).Copy
          srcWS.Range("A" & FindLastRow(srcWS) + 1).PasteSpecial xlPasteAll
          End If
          Next r
          End If
          End If
          Next sh
          End Sub

          Private Function IsInArray(ByVal stringToBeFound As String, _
          ByRef arr As Variant) As Boolean
          IsInArray = (UBound(Filter(arr, stringToBeFound)) > -1)
          End Function

          Private Function FindLastRow(ByRef thisWS As Worksheet) As Long
          With thisWS
          FindLastRow = .Cells(.Cells.Rows.Count, 1).End(xlUp).Row
          End With
          End Function

          Private Function RowMatchesFilter(ByRef thisRow As Range, _
          ByRef thisFilter As Range) As Boolean
          '--- the row matches only if the value in thisRow equals the value
          ' in the filter
          RowMatchesFilter = True
          Dim i As Long
          For i = 1 To 16
          If Not IsEmpty(thisFilter.Cells(1, i).Value) Then
          If thisRow.Cells(1, i).Value <> thisFilter.Cells(1, i).Value Then
          '--- the first cell that doesn't match invalidates the
          ' entire row
          RowMatchesFilter = False
          Exit For
          End If
          End If
          Next i
          End Function





          share|improve this answer













          So here is almost your same approach, just reworked into a form that isolates each step of your process clarify what you want to accomplish. Having a nested loop is not a problem, as long as you keep track of what you're trying to do. What I do want to steer you away from is using GoTo statements. They are almost never necessary.



          So first things first...



          Always use Option Explicit and declare your variables as close as possible to where you want to use them. This habit makes it easier to understand what each variable is and what it's used for. If you declare them all at the top, you'll always be popping back and forth to find them.



          Option Explicit

          Sub CopyFilteredData()
          Dim srcWB As Workbook
          Dim srcWS As Worksheet
          Set srcWB = ActiveWorkbook
          Set srcWS = srcWB.Sheets("Summary (Filtered)")


          Since you will always be referring to your filter in the same location, just define a variable that specifically matches your filter. The bonus here is if your filter changes from row 7 to row 8 (for example), you only have to change it in one spot.



              Dim srcFilter As Range
          Set srcFilter = srcWS.Range("A7").Resize(1, 16)


          Using the same idea, set up a variable that clearly defines the worksheets to skip:



              Dim skipTheseSheets As Variant
          skipTheseSheets = Array(srcWS.Name, "List Data", "Summary (All)", "Lists")
          Dim sh As Worksheet
          For Each sh In srcWB.Sheets
          If Not IsInArray(sh.Name, skipTheseSheets) Then


          This answer gives an excellent function to check if your worksheet name exists in that array.



          You didn't include your function for LastRow, so I included it in my answer. However, make a habit of naming your functions using a verb that is descriptive of what the function does. In this case FindLastRow.



          In order to stop using GoTo statements, just reverse the If statement and proceed if it passes:



          Dim lastRow As Long
          lastRow = FindLastRow(sh)
          If lastRow > 7 Then


          I created a separate function that compares a given row against your filter. It uses basically your same logic, but by isolating it as a function, it makes your main logic read more simply. Also, notice that you can exit a For loop and avoid the dreaded GoTo:



          Private Function RowMatchesFilter(ByRef thisRow As Range, _
          ByRef thisFilter As Range) As Boolean
          '--- the row matches only if the value in thisRow equals the value
          ' in the filter
          RowMatchesFilter = True
          Dim i As Long
          For i = 1 To 16
          If Not IsEmpty(thisFilter.Cells(1, i).Value) Then
          If thisRow.Cells(1, i).Value <> thisFilter.Cells(1, i).Value Then
          '--- the first cell that doesn't match invalidates the
          ' entire row
          RowMatchesFilter = False
          Exit For
          End If
          End If
          Next i
          End Function


          So your copy loop ends up looking like this:



          Dim r As Long
          For r = lastRow To 7 Step -1
          If RowMatchesFilter(sh.Rows(r), srcFilter) Then
          sh.Rows(r).Copy
          srcWS.Range("A" & FindLastRow(srcWS) + 1).PasteSpecial xlPasteAll
          End If
          Next r


          Here is the whole module:



          Option Explicit

          Sub CopyFilteredData()
          Dim srcWB As Workbook
          Dim srcWS As Worksheet
          Set srcWB = ActiveWorkbook
          Set srcWS = srcWB.Sheets("Summary (Filtered)")

          Dim srcFilter As Range
          Set srcFilter = srcWS.Range("A7").Resize(1, 16)

          Dim skipTheseSheets As Variant
          skipTheseSheets = Array(srcWS.Name, "List Data", "Summary (All)", "Lists")

          Dim sh As Worksheet
          For Each sh In srcWB.Sheets
          If Not IsInArray(sh.Name, skipTheseSheets) Then
          Dim lastRow As Long
          lastRow = FindLastRow(sh)
          If lastRow > 7 Then
          '--- now copy the data from this sheet back to the source
          ' in reverse order, using the source filter line to
          ' direct which cells to copy
          Dim r As Long
          For r = lastRow To 7 Step -1
          If RowMatchesFilter(sh.Rows(r), srcFilter) Then
          sh.Rows(r).Copy
          srcWS.Range("A" & FindLastRow(srcWS) + 1).PasteSpecial xlPasteAll
          End If
          Next r
          End If
          End If
          Next sh
          End Sub

          Private Function IsInArray(ByVal stringToBeFound As String, _
          ByRef arr As Variant) As Boolean
          IsInArray = (UBound(Filter(arr, stringToBeFound)) > -1)
          End Function

          Private Function FindLastRow(ByRef thisWS As Worksheet) As Long
          With thisWS
          FindLastRow = .Cells(.Cells.Rows.Count, 1).End(xlUp).Row
          End With
          End Function

          Private Function RowMatchesFilter(ByRef thisRow As Range, _
          ByRef thisFilter As Range) As Boolean
          '--- the row matches only if the value in thisRow equals the value
          ' in the filter
          RowMatchesFilter = True
          Dim i As Long
          For i = 1 To 16
          If Not IsEmpty(thisFilter.Cells(1, i).Value) Then
          If thisRow.Cells(1, i).Value <> thisFilter.Cells(1, i).Value Then
          '--- the first cell that doesn't match invalidates the
          ' entire row
          RowMatchesFilter = False
          Exit For
          End If
          End If
          Next i
          End Function






          share|improve this answer












          share|improve this answer



          share|improve this answer










          answered Jan 2 at 14:48









          PeterTPeterT

          4,3981931




          4,3981931













          • That's brilliant, man - works like a dram, thank you! I don't have any additional questions. The only issue was with FindLastRow function but I solved it. Great work!

            – John
            Jan 2 at 15:35













          • Hello, I would like to reopen the thread as there is one problem which I didn't anticipate. As of now, the macro has problem when in source tabs, the different values appear based on formula. Namely it is either "Check" or blank (""). Once in filter line there is only those with Check in source tab, the macro doesn't return correct data (also brings up blanks, but not all of them) I would really appreciate your help on that one.

            – John
            Jan 23 at 13:39











          • I'm a little unclear what you're asking, and since it seems like a separate question it's best to go ahead and create a new question. You can link back to this post if you like, but still share example data and code so folks here can have the best chance to get you a quick answer.

            – PeterT
            Jan 23 at 17:43











          • I've created another question with the reference to this one as the problem may be considered as different. You may find it here: stackoverflow.com/questions/54365213/…

            – John
            Jan 25 at 12:19





















          • That's brilliant, man - works like a dram, thank you! I don't have any additional questions. The only issue was with FindLastRow function but I solved it. Great work!

            – John
            Jan 2 at 15:35













          • Hello, I would like to reopen the thread as there is one problem which I didn't anticipate. As of now, the macro has problem when in source tabs, the different values appear based on formula. Namely it is either "Check" or blank (""). Once in filter line there is only those with Check in source tab, the macro doesn't return correct data (also brings up blanks, but not all of them) I would really appreciate your help on that one.

            – John
            Jan 23 at 13:39











          • I'm a little unclear what you're asking, and since it seems like a separate question it's best to go ahead and create a new question. You can link back to this post if you like, but still share example data and code so folks here can have the best chance to get you a quick answer.

            – PeterT
            Jan 23 at 17:43











          • I've created another question with the reference to this one as the problem may be considered as different. You may find it here: stackoverflow.com/questions/54365213/…

            – John
            Jan 25 at 12:19



















          That's brilliant, man - works like a dram, thank you! I don't have any additional questions. The only issue was with FindLastRow function but I solved it. Great work!

          – John
          Jan 2 at 15:35







          That's brilliant, man - works like a dram, thank you! I don't have any additional questions. The only issue was with FindLastRow function but I solved it. Great work!

          – John
          Jan 2 at 15:35















          Hello, I would like to reopen the thread as there is one problem which I didn't anticipate. As of now, the macro has problem when in source tabs, the different values appear based on formula. Namely it is either "Check" or blank (""). Once in filter line there is only those with Check in source tab, the macro doesn't return correct data (also brings up blanks, but not all of them) I would really appreciate your help on that one.

          – John
          Jan 23 at 13:39





          Hello, I would like to reopen the thread as there is one problem which I didn't anticipate. As of now, the macro has problem when in source tabs, the different values appear based on formula. Namely it is either "Check" or blank (""). Once in filter line there is only those with Check in source tab, the macro doesn't return correct data (also brings up blanks, but not all of them) I would really appreciate your help on that one.

          – John
          Jan 23 at 13:39













          I'm a little unclear what you're asking, and since it seems like a separate question it's best to go ahead and create a new question. You can link back to this post if you like, but still share example data and code so folks here can have the best chance to get you a quick answer.

          – PeterT
          Jan 23 at 17:43





          I'm a little unclear what you're asking, and since it seems like a separate question it's best to go ahead and create a new question. You can link back to this post if you like, but still share example data and code so folks here can have the best chance to get you a quick answer.

          – PeterT
          Jan 23 at 17:43













          I've created another question with the reference to this one as the problem may be considered as different. You may find it here: stackoverflow.com/questions/54365213/…

          – John
          Jan 25 at 12:19







          I've created another question with the reference to this one as the problem may be considered as different. You may find it here: stackoverflow.com/questions/54365213/…

          – John
          Jan 25 at 12:19






















          draft saved

          draft discarded




















































          Thanks for contributing an answer to Stack Overflow!


          • Please be sure to answer the question. Provide details and share your research!

          But avoid



          • Asking for help, clarification, or responding to other answers.

          • Making statements based on opinion; back them up with references or personal experience.


          To learn more, see our tips on writing great answers.




          draft saved


          draft discarded














          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54007190%2fmacro-for-copying-data-from-tabs-based-on-filtered-line%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown





















































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown

































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown







          Popular posts from this blog

          MongoDB - Not Authorized To Execute Command

          How to fix TextFormField cause rebuild widget in Flutter

          in spring boot 2.1 many test slices are not allowed anymore due to multiple @BootstrapWith