Is there an efficient way to update data from one sheet to another with a match of a cell - excel

I have a sheet that has a main tab and a data tab. I update the data tab daily by copying from a daily report I get automatically from a reporting portal. I dump that data into the data tab and have written some code to update some of the columns in the main tab. The code matches the loan number in column C, if a match is found it executes the copy and paste.
The code works perfectly but it is slow as I've added other columns to copy.
I am asking the experts to review my code and maybe show me a more efficient way of writing the code so it can run faster. The data it is searching through is only a couple of hundred rows, I don't think it should take too long.
Here is my code:
Sub Update_Data()
ActiveSheet.Unprotect Password:="Mortgage1"
Application.ScreenUpdating = False
Dim stNow As Date
Dim sourceRng As Range
Dim destRng As Range
stNow = Now
lrowloans = Worksheets("Main").Range("A6").End(xlDown).Row
lrowdata = Worksheets("Data").Range("C11").End(xlDown).Row
Set sourceRng = Worksheets("Main").Range("A6:A" & lrowloans)
Set destRng = Worksheets("Data").Range("C11:C" & lrowdata)
Dim match As Boolean
For Each sRng In sourceRng
If sRng.Value <> "" Then
With destRng
Set dRng = .Find(What:=sRng.Value, After:=Worksheets("Data").Range("C11"), LookIn:=xlValues, LookAt:=xlPart, SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:=False, SearchFormat:=False)
If Not dRng Is Nothing Then
Set pasteRng = Worksheets("Main").Range("E" & sRng.Row)
Set copyRng = Worksheets("Data").Range("G" & dRng.Row & ":H" & dRng.Row)
copyRng.Copy pasteRng
Set pasteRng = Worksheets("Main").Range("B" & sRng.Row)
Set copyRng = Worksheets("Data").Range("D" & dRng.Row & ":E" & dRng.Row)
copyRng.Copy pasteRng
Set pasteRng = Worksheets("Main").Range("D" & sRng.Row)
Set copyRng = Worksheets("Data").Range("U" & dRng.Row & ":U" & dRng.Row)
copyRng.Copy pasteRng
Set pasteRng = Worksheets("Main").Range("M" & sRng.Row)
Set copyRng = Worksheets("Data").Range("Q" & dRng.Row & ":Q" & dRng.Row)
copyRng.Copy pasteRng
Set pasteRng = Worksheets("Main").Range("K" & sRng.Row)
Set copyRng = Worksheets("Data").Range("AP" & dRng.Row & ":AP" & dRng.Row)
copyRng.Copy pasteRng
Set pasteRng = Worksheets("Main").Range("N" & sRng.Row)
Set copyRng = Worksheets("Data").Range("AW" & dRng.Row & ":AW" & dRng.Row)
copyRng.Copy pasteRng
End If
End With
End If
Next
Application.ScreenUpdating = True
ActiveSheet.Protect Password:="Mortgage1"
End Sub

This will run just about as fast as your computer can do the job. Any difference is achieved by reading from the sheets less frequently. I didn't find any significant sources for delay in your code, such as avoidable loops.
Option Explicit
' the assigned numbers are Excel's column numbers
' Test: Debug.Print Columns(NmcName).Address(0,0)
Enum Ndc ' "Data" columns enumeration
' 132 - 08 Dec 2020
NdcName = 4 ' 4 = column D
NdcProc ' "Processor"
NdcPurp = 7 ' "Purpose"
NdcProd ' "Product type"
NdcLockX = 17 ' "Lock Expiry"
NdcLoan = 21 ' "Loan amount"
NdcCD = 42 ' "CD issued"
NdcClose = 49 ' "Closing date"
End Enum
Enum Nmc ' "Main" columns
' 132 - 08 Dec 2020
NmcName = 2 ' 2 = column B
NmcProc ' "Processor"
NmcLoan ' "Loan amount"
NmcPurp ' "Purpose"
NmcProd ' "Product type"
NmcCD = 11 ' "CD issued"
NmcLockX = 13 ' "Lock Expiry"
NmcClose ' "Closing date"
End Enum
Sub Update_Data()
' 132 - 08 Dec 2020
Const pWord As String = "Mortgage1"
Dim WsMain As Worksheet
Dim WsData As Worksheet
Dim sourceRng As Range
Dim destRng As Range
Dim sCell As Range ' loop object
Dim Fnd As Range ' cell found by Find
Dim SrcArr As Variant ' data from Fnd.Row
Dim SrcClm As Variant ' array of source columns
Dim DstClm As Variant ' array of destination columns
Dim C As Long ' loop counter: column
Set WsMain = Worksheets("Main")
Set WsData = Worksheets("Data")
With WsMain
.Unprotect Password:=pWord ' presuming WsMain is your AciveSheet
Set sourceRng = .Range(.Cells(6, "A"), .Cells(.Rows.Count, "A").End(xlUp))
End With
With WsData
Set destRng = .Range(.Cells(11, "C"), .Cells(.Rows.Count, "C").End(xlUp))
End With
' data will be copied from SrcClm to DstClm, like NdcPurp to NmcPurp
' sequence is immaterial but position must match
' number of columns in both arrays must be identical
' effect modifications in the Enum
SrcClm = Array(NdcPurp, NdcProd, NdcName, NdcProc, NdcLoan, NdcLockX, NdcCD, NdcClose)
DstClm = Array(NmcPurp, NmcProd, NmcName, NmcProc, NmcLoan, NmcLockX, NmcCD, NmcClose)
Application.ScreenUpdating = False
For Each sCell In sourceRng
If sCell.Value <> "" Then
With destRng
Set Fnd = .Find(What:=sCell.Value, _
After:=.Cells(1), _
LookIn:=xlValues, _
LookAt:=xlPart, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext, _
MatchCase:=False, _
SearchFormat:=False)
If Not Fnd Is Nothing Then
SrcArr = .Range(.Cells(Fnd.Row, 1), .Cells(Fnd.Row, NdcClose)).Value
For C = LBound(SrcClm) To UBound(SrcClm)
WsMain.Cells(sCell.Row, DstClm(C)).Value = SrcArr(1, SrcClm(C))
Next C
End If
End With
End If
Next sCell
With Application
.CutCopyMode = False
.ScreenUpdating = True
End With
WsMain.Protect Password:=pWord
End Sub
The code's syntax has been tested but, for lack of data, not its functionality. Especially the coordination of enumerations Ndc and Nmc with the arrays SrcClm and DstClm may write some values to the wrong columns. Here's how you can fix that. Look for these two arrays.
SrcClm = Array(NdcPurp, NdcProd, NdcName, NdcProc, NdcLoan, NdcLockX, NdcCD, NdcClose)
DstClm = Array(NmcPurp, NmcProd, NmcName, NmcProc, NmcLoan, NmcLockX, NmcCD, NmcClose)
SrcClm lists all used columns in the source sheet. DstClm lists all used columns in the destination sheet. You can add, delete or change either. The sequence is immaterial. But for every Source cell there must be a destination cell. Therefore the number of columns in both arrays must always be the same.
The arrays specify columns. The rows will be found by the code. The Source row is determined one after the other, in a loop. The Destination row is Fnd.Row. Now the code works through the two arrays. It takes the first column from SrcClm, finds the cell with the help of the provided row and pastes it to the first column from the DstClm array in the Fnd.Row row.
Example:- The first SrcClm is NdcG which was given a value of 7 (column G) in the enumeration. The first DstClmis NmcE which has a value of 5 (column E) in the enumeration. Now, presume instead of reading from column G you wanted to read from column H. So you start from the Enumeration. Change the assigned value from 7 to 8. Note that this change would automatically also change the value of NdcH. This is because NdcH as no value assigned to it which VBA understands to mean "next number". So, when you change NdcG to , NdcH will become 9 and you may have to change that, too.
After you change the value of NdcG the code will read from column H, just as you wanted, but the enumeration's name is wrong. Had we given descriptive names to the enumerations, like NdcName, NdcDob and NdcContractID this problem wouldn't exist. But as it is now NdcG must be changed to NdcH and you can't do that before you change the existing NdcH to NdcI.
Anyway, don't change the name in the enumeration. Instead, use Edit > Replace and replace all occurrences of NdcH with NdcI and, thereafter, all occurrences of NdcG to NdcH. All of this sounds complicated and it is that. You need to give your full attention to avoid mistakes.
Of course, in this way you can change any source or destination column in the system. It's not difficult and can be completed quite fast after you have done it once.

Related

VBA why do I have blank rows after appending tables?

VBA newb here.
Essentially, I'm collecting weekly compliance records for week over week data.
My main issue is that I have a queried table that is dynamic and on a good week it's empty.
I would like to be able to pull the contents of this table and paste them to the first empty row below a static table that contains year to date data.
This step is an easy one to accomplish manually, but I'm looking to automate for the sake of handing this report off to my less-than-tech-savvy team members.
This question: How to copy and paste two separate tables to the end of another table in VBA? has given me most of what I'm using so far. I've swapped a few of their values and declarations to be relevant to my sheet and ranges, but for the most part it's copy/paste with the listed solution for "Destination: ="
For the most part, this block does the exact thing I'm after:
(I've commented out GCC's second range, but intend to utilize it once this one's settled.)
Sub Inv_Copy_Paste()
Dim TC As Worksheet
'Dim Chart As Worksheet
Dim lr2 As Long
Set TC = Worksheets("TC Data Dump")
'Set Chart = Worksheets("Inventory for Charts")
lr2 = TC.Cells(Rows.Count, 1).End(xlUp).Row
With TC
.Range("O2", ("W2" & .Range("O" & Rows.Count).End(xlUp).Row)).Copy Destination:=TC.Cells(Rows.Count, 1).End(xlUp).Offset(1)
'.Range("K2", ("S2" & .Range("K" & Rows.Count).End(xlUp).Row)).Copy Destination:=Chart.Range("A" & lr2 + 1)
End With
End Sub
The one exception that I'm running into is that once the code copies populated data over, it adds a handful of blank lines below the data:
20 Blank Rows
Is this something I'm overlooking in the code that's already here?
I'll grant that I barely understand what the code is doing in the With TC portion, so any additional context would be greatly appreciated.
Bonus question: Will I need a separate Sub/Worksheet when I attempt to copy another dynamic query table to a second static table?
Dealing With Blanks
If your data is in Excel tables, you should use their methods and properties.
If you don't wanna, you'll need to write special, often complicated codes.
End(xlUp) will only go up to the last row (cell) in the table. If there are empty or blank rows at the bottom, they will also be copied.
The Find method with xlFormulas will go up to the last non-empty row while with xlValues, it will go up (further) to the last non-blank row.
Initial
Result
Main
Sub InvCopyPaste()
Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing this code
Dim wsTC As Worksheet: Set wsTC = wb.Sheets("TC Data Dump")
Dim wsInv As Worksheet: Set wsInv = wb.Sheets("Inventory for Charts")
Dim srg As Range, drg As Range
' Source: 'wsTC' to Destination: 'wsTC'
Set srg = RefNonBlankRange(wsTC.Range("O2:W2"))
If Not srg Is Nothing Then
Set drg = RefFirstNonBlankRowRange(wsTC.Range("A2") _
.Resize(, srg.Columns.Count)).Resize(srg.Rows.Count)
drg.Value = srg.Value ' for only values (most efficient)
'srg.Copy drg ' instead: for values, formulas and formats
Debug.Print "Copied from " & srg.Address & " to " & drg.Address & "."
End If
' Source: 'wsTC' to Destination: 'wsInv'
Set srg = RefNonBlankRange(wsTC.Range("K2:S2"))
If Not srg Is Nothing Then
Set drg = RefFirstNonBlankRowRange(wsInv.Range("A2") _
.Resize(, srg.Columns.Count)).Resize(srg.Rows.Count)
drg.Value = srg.Value ' for only values (most efficient)
'srg.Copy drg ' instead: for values, formulas and formats
Debug.Print "Copied from " & srg.Address & " to " & drg.Address & "."
End If
End Sub
The Help
Function RefNonBlankRange( _
ByVal FirstRowRange As Range) _
As Range
With FirstRowRange
Dim cel As Range: Set cel = .Resize(.Worksheet.Rows.Count - .Row + 1) _
.Find("*", , xlValues, , xlByRows, xlPrevious)
If Not cel Is Nothing _
Then Set RefNonBlankRange = .Resize(cel.Row - .Row + 1)
End With
End Function
Function RefFirstNonBlankRowRange( _
ByVal FirstRowRange As Range) _
As Range
Dim rg As Range: Set rg = FirstRowRange.Rows(1)
With rg
Dim cel As Range: Set cel = .Resize(.Worksheet.Rows.Count - .Row + 1) _
.Find("*", , xlValues, , xlByRows, xlPrevious)
If Not cel Is Nothing Then Set rg = .Offset(cel.Row - .Row + 1)
End With
Set RefFirstNonBlankRowRange = rg
End Function
Debug.Print Results in the Immediate window (Ctrl+G)
Copied from $O$2:$W$6 to $A$4:$I$8.
Copied from $K$2:$S$6 to $A$6:$I$10.
Firstly, the row count is counting the number of lines in the first column.
-lr2 = TC.Cells(Rows.Count, 1).End(xlUp).Row
Here.
Rather than counting the number of rows in the tablese you're trying to copy.
If you change the number 1 in this line to the column you are copying. I think its "O" which would be 15.
Then I'm afraid you'd have to redefine the lr2 for the second table or make another variable for it.
lr3 = TC.Cells(Rows.Count, 11).End(xlUp).Row '11 for the k column
Please let me know if this helps.
Sub oddzac()
Dim RowCount As Integer
ActiveSheet.Range("O2", Cells(Range("W" & Rows.Count).End(xlUp).Row, "W")).Copy Cells(Range("A" & Rows.Count).End(xlUp).Row, 1)
ActiveSheet.Range("K2", Cells(Range("S" & Rows.Count).End(xlUp).Row, "S")).Copy Cells(Range("A" & Rows.Count).End(xlUp).Row + 1, 1)
End Sub
This more what you're looking for?
Another forum responded with this solution:
Sub TC_Copy_Paste()
Dim TC As Worksheet, RowNum As Long
'
Set TC = Worksheets("TC Data Dump")
On Error Resume Next
With TC.Range("P3").ListObject
RowNum = Application.WorksheetFunction.CountA(.ListColumns(1).DataBodyRange)
.DataBodyRange.Cells(1, 1).Resize(RowNum, 9).Copy Destination:=TC.Cells(Rows.Count, 5).End(xlUp).Offset(1)
End With
With TC.Range("AJ3").ListObject
RowNum = Application.WorksheetFunction.CountA(.ListColumns(1).DataBodyRange)
.DataBodyRange.Cells(1, 1).Resize(RowNum, 9).Copy Destination:=TC.Cells(Rows.Count, 26).End(xlUp).Offset(1)
End With
End Sub
Again, I'm not sure why this works and the other doesn't but I wanted to share the end result.

Comparing all cells in 2 different sheets and finding mismatch list isn't working

I have a data set with columns from A to AZ. I want to find if any cell value in Columns A & B is found in Columns AA:AZ and I want a list of those unique not found values from all the compared columns.
What I did first is create 2 new sheets to separate the comparison. 1st sheet (SKUReference) which is copied from column A & B. Second sheet is (SKUNewList) which is copied from AA till AZ. I created a 3rd sheet (NotFoundSKU) to have the desired output which is the Not Found values from the comparison.
The data in the 1st sheet (SKUReference) looks like below :
The data in the 2nd sheet (SKUNewList) looks like below :
The issue I'm facing is : 1- the code isn't finding the Mismatches. 2- It's not storing the unique mismatches correctly. 3- It's not generating those mismatches in the 3rd sheet (NotFoundSKU).
Sub yg23iwyg()
Dim wst As Worksheet
Dim wet As Worksheet
Set wst = Worksheets.Add
Set wet = Worksheets.Add
Set wrt = Worksheets.Add
wst.Name = "SKUReference"
wet.Name = "SKUNewList"
wrt.Name = "NotFoundSKU"
With Worksheets("Sheet1")
.Range(.Cells(1, "A"), .Cells(.Rows.Count, "B").End(xlUp)).Copy _
Destination:=wst.Cells(1, "A")
.Range(.Cells(1, "AA"), .Cells(.Rows.Count, "AZ").End(xlUp)).Copy _
Destination:=wet.Cells(1, "A")
'alternate with Union'ed range - becomes a Copy, Paste Special, Values and Formats because of the union
.Range("AA:AZ").Copy _
Destination:=wet.Cells(1, "A")
End With
Dim wksMaster As Worksheet, wksSearch As Worksheet
Dim rngMaster As Range, rngSearch As Range
Set wksMaster = Sheets("SKUReference")
Set wksSearch = Sheets("SKUNewList")
With wksMaster
Set rngMaster = .Range("A1:B100" & .Range("A1").SpecialCells(xlCellTypeLastCell).Row)
End With
With wksSearch
Set rngSearch = .Range("A1:Y100" & .Range("A1").SpecialCells(xlCellTypeLastCell).Row)
End With
With rngMaster
For Each cll In rngSearch
Set c = .Find(cll.Value2, LookIn:=xlValues)
If c Is Nothing Then
'MsgBox cll.Value2 & " not found in the SKU Reference List."
Sheets("NotFoundSKU").Range("A1") = cll.Value2
End If
Next
End With
End Sub
Try this, which incorporates comments above (to set rngMaster and rngSearch) and will list values not found in a list going down by finding the first empty cell.
Sub yg23iwyg()
Dim wst As Worksheet
Dim wet As Worksheet, c as range, cll as range
Set wst = Worksheets.Add
Set wet = Worksheets.Add
Set wrt = Worksheets.Add
wst.Name = "SKUReference"
wet.Name = "SKUNewList"
wrt.Name = "NotFoundSKU"
With Worksheets("Sheet1")
.Range(.Cells(1, "A"), .Cells(.Rows.Count, "B").End(xlUp)).Copy _
Destination:=wst.Cells(1, "A")
.Range(.Cells(1, "AA"), .Cells(.Rows.Count, "AZ").End(xlUp)).Copy _
Destination:=wet.Cells(1, "A")
'alternate with Union'ed range - becomes a Copy, Paste Special, Values and Formats because of the union
.Range("AA:AZ").Copy _
Destination:=wet.Cells(1, "A")
End With
Dim wksMaster As Worksheet, wksSearch As Worksheet
Dim rngMaster As Range, rngSearch As Range
Set wksMaster = Sheets("SKUReference")
Set wksSearch = Sheets("SKUNewList")
With wksMaster
Set rngMaster = .Range("A1:B" & .Range("B" & .Rows.Count).End(xlUp).Row)
End With
With wksSearch
Set rngSearch = .Range("A1:Y" & .Range("Y" & .Rows.Count).End(xlUp).Row)
End With
With rngMaster
For Each cll In rngSearch
Set c = .Find(cll.Value2, LookIn:=xlValues) 'i would consider adding more parameters here
If c Is Nothing Then
Sheets("NotFoundSKU").Range("A" & Rows.Count).End(xlUp)(2).Value = cll.Value2
End If
Next
End With
End Sub

How to fix 'Run-time error '1004' PasteSpecial

I have a file (called original) that has partially information for each row. Each row has a file name column (from where information is to be captured from).
For each row I'd like to open up the file in the file name column, and grab information from certain rows.
In the file it is only one column, with rows "Supplier Number : _____", the location of this row is variable, so I'd like to iterate through each row in the file to copy this cell value and paste it into the original file in the corresponding row.
This is what I have so far:
Const FOLDER_PATH = "C:\Users\[user]\Downloads\"
Sub iterateThroughAll()
ScreenUpdating = False
Dim wks As Worksheet
Set wks = ActiveSheet
Dim source As String
Dim target As String
Dim update As String
Dim rowT As Integer
rowT = 2
rowTT = 1
Dim rowRange As Range
Dim colRange As Range
Dim rowRangeT As Range
Dim LastCol As Long
Dim LastRow As Long
Dim LastRowT As Long
LastRow = wks.Cells(wks.Rows.Count, "A").End(xlUp).Row
Set rowRange = wks.Range("A2:A" & LastRow)
'Loop through each row
For Each rrow In rowRange
source = FOLDER_PATH & wks.Cells(i, 18).Value 'the name of the file we want to grab info from in this Column, always populated
'if the cell is empty, search through the file for "Supplier Number : "
If IsEmpty(wks.Cells(rowT, 19)) Then
Set wb = Workbooks.Open(source)
wb.Activate
LastRowT = Cells.Find("*", searchorder:=xlByRows, searchdirection:=xlPrevious).Row
Set rowRangeT = wks.Range("A1:A" & LastRowT)
For Each i In rowRangeT
If InStr(i.Cells.Offset(rowTT), "Supplier") > 0 Then
Range("A" & rowTT).Select
Selection.Copy
Windows("Get Supplier Number.xlsm").Activate
Range("A" & rowT).Select
wks.Paste
Else
rowTT = rowTT + 1
End If
Next i
wb.Close
Next rrow
ScreenUpdating = True
End Sub
I get the pastespecial error 1004.
What is expected is that for each row in "Get Supplier Number.xlsm", the row's A column is updated with the information
Thank you for helping!
First of all you should get rid of Activate and Select methods. You don't have to use them and they give nothing to your code. Using them is not a good approach.
To avoid them you should use specific references. Which you are doing so, until a specific point. Inside the for loop, after setting the wb, replace everything with the following:
With wb.Worksheets(1)
LastRowT = .Cells.Find("*", searchorder:=xlByRows, searchdirection:=xlPrevious).Row
Set rowRangeT = .Range("A1:A" & LastRowT)
For Each i In rowRangeT
If InStr(i.Cells.Offset(rowTT), "Supplier") > 0 Then
.Range("A" & rowTT).Copy wks.Range("A" & rowT)
Else
rowTT = rowTT + 1
End If
Next i
wb.Close
End With
I think this should do the job for you.
PS: If you need just the value of the cell in the opened workbook, then you could replace the Copy line with a simple equality:
wks.Range("A" & rowT) = .Range("A" & rowTT)

Find Cell containing text in column and does NOT contain certain word in first 6 characters of string

I am searching a column for cell that contains text and does not contain the word "cat" in the first 6 characters (needs to be case insensitive). This will then cut that entire row to another sheet. Cannot get the code to run without compile errors. the below code is before i try to change it. I do not know how to code it to look at the first 6 characters.
tried instr & iserror but i think my existing code just needs a small alteration which escapes me.
Sub CATDEFECTS()
UsdRws = Range("C" & Rows.Count).End(xlUp).Row
For i = UsdRws To 2 Step -1
If Range("C" & i).Value Like "<>""" And Range("c" & i).Value Like "CAT" Then
Rows(i).Cut Sheets("AWP DEFECTS").Range("A" & rows.Count).End(xlUp).Offset(1)
Rows(i).Delete
End If
Next i
End Sub
Regardless of how you decide to implement the macro, your test to see if a cell is blank is entirely redundant. You can just test if the cell meets your CAT criteria. If it does, it is definitely not blank so no need to test it.
Method 1
You can look at the first 6 characters with LEFT(Range, 6)
If Left(Range("C" & i), 6) Like "*CAT*" Then
This needs Option Compare to work (Thanks #Comintern)
Method 2
I would prefer this method. Its explicit and does not delete or shift anything inside the loop so your action statements are greatly minimized.
Sub Cat()
Dim ws As Worksheet: Set ws = ThisWorkbook.Sheets("Sheet1") '<--UPDATE
Dim ps As Worksheet: Set ps = ThisWorkbook.Sheets("AWP DEFECTS")
Dim LR As Long, DeleteMe As Range, i As Long
LR = ws.Range("C" & ws.Rows.Count).End(xlUp).Row
For i = 2 To LR
If InStr(Left(ws.Range("C" & i), 6), "CAT") Then
If Not DeleteMe Is Nothing Then
Set DeleteMe = Union(DeleteMe, ws.Range("C" & i))
Else
Set DeleteMe = ws.Range("C" & i)
End If
End If
Next i
Application.ScreenUpdating = False
If Not DeleteMe Is Nothing Then
LR = ps.Range("A" & ps.Rows.Count).End(xlUp).Row
DeleteMe.EntireRow.Copy ps.Range("A" & LR)
DeleteMe.EntireRow.Delete
End If
Application.ScreenUpdating = True
End Sub
If cat is within the first 6 characters then InStr will report its position being less than 5.
Sub CATDEFECTS()
dim UsdRws as long, pos as long
UsdRws = Range("C" & Rows.Count).End(xlUp).Row
For i = UsdRws To 2 Step -1
pos =instr(1, cells(i, "C").value2, "cat", vbtextcompare)
If pos > 0 and pos < 5 Then
Rows(i).Cut Sheets("AWP DEFECTS").Range("A" & rows.Count).End(xlUp).Offset(1)
Rows(i).Delete
End If
Next i
End Sub
Criteria Backup (Hide/Delete)
To enable the deletion of the rows in the Source Worksheet you have to set cDEL to True in the constants section. Adjust the other constants to fit you needs.
The Code
Option Explicit
'Option Compare Text
Sub CATDEFECTS()
With Application
.ScreenUpdating = False
.Calculation = xlCalculationManual
End With
On Error GoTo ProcedureExit
' Source Constants
Const cSource As Variant = "Sheet1" ' Worksheet Name/Index
Const cCol As Variant = "C" ' Search Column Letter/Number
Const cFirstR As Long = 2 ' First Row Number
Const cChars As Long = 6 ' Number of Chars
Const cSearch As String = "CAT" ' Search String
' Target Constants
Const cTarget As Variant = "AWP DEFECTS" ' Worksheet Name/Index
Const cColTgt As Variant = "A" ' Column Letter/Number
Const cFirstRTgt As Long = 2 ' First Row Number
Const cDEL As Boolean = False ' Enable Delete (True)
' Variables
Dim rngH As Range ' Help Range
Dim rngU As Range ' Union Range
Dim vntS As Variant ' Source Array
Dim i As Long ' Source Range Row Counter
' The Criteria
' When the first "cChars" characters do not contain the case-INsensitive
' string "cSearch", the criteria is met.
' Source Worksheet
With ThisWorkbook.Worksheets(cSource)
' Calculate Last Cell in Search Column using the Find method and
' assign it to Help (Cell) Range.
Set rngH = .Columns(cCol).Find("*", , xlFormulas, _
xlWhole, xlByColumns, xlPrevious)
' Calculate Source Column Range from Help (Cell) Range.
If Not rngH Is Nothing Then ' Last Cell was found.
' Calculate Source Column Range and assign it to
' Help (Column) Range using the Resize method.
Set rngH = .Cells(cFirstR, cCol).Resize(rngH.Row - cFirstR + 1)
' Copy Help (Column) Range into 2D 1-based 1-column Source Array.
vntS = rngH
' Show hidden rows to prevent the resulting rows (the rows to be
' hidden or deleted) to appear hidden in Target Worksheet.
rngH.EntireRow.Hidden = False
Else ' Last Cell was NOT found (unlikely).
MsgBox "Empty Column '" & cCol & "'."
GoTo ProcedureExit
End If
' Loop through rows of Source Array.
For i = 1 To UBound(vntS)
' Check if current Source Array value doesn't meet Criteria.
If InStr(1, Left(vntS(i, 1), cChars), cSearch, vbTextCompare) = 0 _
Then ' "vbUseCompareOption" if "Option Compare Text"
' Note: To use the Like operator instead of the InStr function
' you have to use (uncomment) "Option Compare Text" at the beginning
' of the module for a case-INsensitive search and then outcomment
' the previous and uncomment the following line.
' If Not Left(vntS(i, 1), cChars) Like "*" & cSearch & "*" Then
Set rngH = .Cells(i + cFirstR - 1, cCol)
If Not rngU Is Nothing Then
' Union Range contains at least one range.
Set rngU = Union(rngU, rngH)
Else
' Union Range does NOT contain a range (only first time).
Set rngU = rngH
End If
End If
Next
End With
' Target Worksheet
If Not rngU Is Nothing Then ' Union Range contains at least one range.
With ThisWorkbook.Worksheets(cTarget)
' Calculate Last Cell in Search Column using the Find method and
' assign it to Help Range.
Set rngH = .Columns(cColTgt).Find("*", , xlFormulas, _
xlWhole, xlByColumns, xlPrevious)
' Calculate Last Cell from Help Range, but in column 1 ("A").
If Not rngH Is Nothing Then ' Last Cell was found.
Set rngH = .Cells(rngH.Row + 1, 1)
Else ' Last Cell was NOT found.
Set rngH = .Cells(cFirstRTgt - 1, 1)
End If
' Copy the entire Union Range to Target Worksheet starting from
' Help Range Row + 1 i.e. the first empty row (in one go).
' Note that you cannot Cut/Paste on multiple selections.
rngU.EntireRow.Copy rngH
End With
' Hide or delete the transferred rows (in one go).
If cDEL Then ' Set the constant cDEL to True to enable Delete.
rngU.EntireRow.Delete
Else ' While testing the code it is better to use Hidden.
rngU.EntireRow.Hidden = True
End If
End If
ProcedureExit:
With Application
.Calculation = xlCalculationAutomatic
.ScreenUpdating = True
End With
End Sub
Remarks
The use of the array did not speed up considerably.
The InStr function was a few milliseconds faster than the Like operator in my data set.
Calculating the Real Used Range and copying it into a Source Array
and then writing the data that meets the criteria from Source Array
to a Target Array and copying the Target Array to the Target
Worksheet, might be faster, and/but would additionally copy the data without formulas or formatting.

Trying to filter data and display it on another sheet, however the data isn't fully copied

im trying to filter some data(sheet="Imported Data") and paste the data that matches to a sheet("Test"). However somehow it doesn't fully work. I have asked this kinda question before, but I've been trying for 3hours now but I can't get it done!
What I want:
- There are 3 single cells which the user can fill in which are the criteria(Collection , System and Tag)
- Collection is a MUST fill in for the user, the others can be left blank if the user wants it so. The result must be the entire row(Column A,B and C)
- If one, two or three criteria are filled in the chosen criterias must all match to copy to the new sheet (so if one criteria is left blank, the result should be all all three criteria. But the one not filled in can be any value).
- If all criteria match, from the sheet="Imported Data" also the value of column E must be copied to sheet("Test"),
this value of column E must be the cell which is in the same row as the matched values.
If you have any questions, fill free to ask... it's a bit hard to explain.
Thanks for the help in advance! This is what I have now:
Option Explicit
Sub FilterButton()
Dim SrcSheet As Worksheet, DestSheet As Worksheet
Dim SourceRange As Range
Dim aCell As Range, bCell As Range
Dim iLastRow As Long, zLastRow As Long
Dim Collection As String, System As String, Tag As String
With Application
.ScreenUpdating = False
.EnableEvents = False
End With
'~~> Set your sheet
Set DestSheet = Sheets("Test")
Set SrcSheet = Sheets("Imported Data")
'~~> Find Last Row in Col A in the source sheet
With SrcSheet
iLastRow = .Range("A" & .Rows.Count).End(xlDown).Row
End With
'~~> Find Last "Available Row for Output" in Col A in the destination sheet
With DestSheet
zLastRow = .Range("A" & .Rows.Count).End(xlUp).Row + 1
End With
'~~> Set your ranges
Set SourceRange = SrcSheet.Range("A2:A" & iLastRow)
'~~> Search values
Collection = Trim(Range("lblImportCollection").Value)
System = Trim(Range("lblImportSystem").Value)
Tag = Trim(Range("lblImportTag").Value)
With SourceRange
'~~> Match 1st Criteria
Set aCell = .Find(What:=Collection, LookIn:=xlValues, _
LookAt:=xlWhole, SearchOrder:=xlByRows, SearchDirection:=xlNext, _
MatchCase:=False, SearchFormat:=False)
'~~> If found
If Not aCell Is Nothing Then
Set bCell = aCell
'~~> Copy A:C. Then match for Crit B and Crit C and remove what is not required
DestSheet.Range("A" & zLastRow & ":" & "C" & zLastRow).Value = _
SrcSheet.Range("A" & aCell.Row & ":" & "C" & aCell.Row).Value
'~~> Match 2nd Criteria
If Len(Trim(System)) = 0 Or _
aCell.Offset(, 1).Value <> System Then _
DestSheet.Range("B" & zLastRow).ClearContents
MsgBox System & " Not Found"
'~~> Match 3rd Criteria
If Len(Trim(Tag)) = 0 Or _
aCell.Offset(, 2).Value <> Tag Then _
DestSheet.Range("C" & zLastRow).ClearContents
MsgBox Tag & " Not Found"
If Not DestSheet.Range("B" & zLastRow).ClearContents Or _
DestSheet.Range("C" & zLastRow).ClearContents Then
'~~> Copy E:E. Then match for Crit B and Crit C and remove what is not required
DestSheet.Range("D" & zLastRow & ":" & "D" & zLastRow).Value = _
SrcSheet.Range("E" & aCell.Row & ":" & "E" & aCell.Row).Value
End If
'~~> Increase last row by 1 for output
zLastRow = zLastRow + 1
Do
Set aCell = .FindNext(After:=aCell)
If Not aCell Is Nothing Then
If aCell.Address = bCell.Address Then Exit Do
'~~> Match 2nd Criteria
If Len(Trim(System)) = 0 Or _
aCell.Offset(, 1).Value <> System Then _
DestSheet.Range("B" & zLastRow).ClearContents
'~~> Match 3rd Criteria
If Len(Trim(Tag)) = 0 Or _
aCell.Offset(, 2).Value <> Tag Then _
DestSheet.Range("C" & zLastRow).ClearContents
'~~> Increase last row by 1 for output
zLastRow = zLastRow + 1
Else
Exit Do
End If
Loop
Else
MsgBox Collection & " not Found"
End If
End With
With Application
.ScreenUpdating = True
.EnableEvents = True
End With
End Sub
I think it would be simpler to use the AdvancedFilter method, but your data setup is important.
I have assumed that your original data has Five columns (A:E), with headers being in Row 1
I have further assumed that the headers in columns A:C are "Collection", "System" and "Tag"
I have also assumed there is nothing of importance on "Test" (If there is, instead of "clearing" the entire worksheet, you can alter the code to only clear the relevant part, perhaps the first four columns.
Set up a criteria range (three columns, two rows) on your Imported Data sheet with the same headings in Row 1 as in columns A:C of your data source. You could use Data Validation to force an entry; or you could code something within the macro itself. Or you could develop a UserForm to populate these cells
After your user fills in the criteria, the macro should copy the relevant data. If all three items are populated, it will delete column D, otherwise, it will delete columns D:E.
If I've made some wrong assumptions about how your data is set up, you may need to delete more columns after doing the Filter.
Option Explicit
Sub FilterButton()
Dim SrcSheet As Worksheet, DestSheet As Worksheet
Dim SourceRange As Range
Dim CriteriaRange As Range
Dim DestRange As Range
With Application
.ScreenUpdating = False
.EnableEvents = False
End With
'~~> Set your sheet
Set DestSheet = Sheets("Test")
Set SrcSheet = Sheets("Imported Data")
'~~> Set your ranges
Set SourceRange = SrcSheet.Range("a1").CurrentRegion
Set CriteriaRange = SrcSheet.Range("H1:J2") 'or wherever
Set DestRange = DestSheet.Range("A1")
'Activate Destination Sheet, Clear it, and run the filter
DestSheet.Activate 'Can only copy filtered data to active sheet
DestSheet.Cells.Clear
SourceRange.AdvancedFilter xlFilterCopy, CriteriaRange, DestRange
'Delete column D always, delete Column E if not three criteria
With DestRange.CurrentRegion
If WorksheetFunction.CountA(CriteriaRange.Rows(2)) <> 3 Then
Range(.Columns(4), .Columns(5)).Delete
Else
.Columns(4).Delete (xlToLeft)
End If
End With
With Application
.ScreenUpdating = True
.EnableEvents = True
End With
End Sub

Resources