Here is a walkthrough of the situation:
My excel has 2 tabs, one called AssetName Sheet and the other called AMP Sheet
After clicking a vba button, the user is able to generate names in column B of the AssetName Sheet
I have another button that generates an AMP ID for the user. The easiest way for me to explain how this works is through an Index - Match function
=IFERROR(INDEX('AMP Sheet'!$L:$L,MATCH("*"&B2&"*", 'AMP Sheet'!$B:$B,0)), "Not Found")
Column B in the AssetName Sheet is named GeneratedAssetName and column L in the AMP Sheet is named ID.
So, in this example, My_Sandwich_6S_HeroRegular_Mobile exists in the AMP Sheet. Since this is a match, it will grab the associated ID from the AMP Sheet and copy it over to column E of the AssetName Sheet:
My logic (which does the exact same thing as the function I listed in step 3) is housed within a VBA macro button. The code is shown below:
Sub AMPTabid()
Dim wsAN As Worksheet
Set wsAN = Sheets("AssetName Sheet")
Dim wsAMP As Worksheet
Set wsAMP = Sheets("AMP Sheet")
Dim LastRow As Long
LastRow = wsAN.Cells(wsAN.Rows.Count, 2).End(xlUp).Row
Dim i As Long
For i = 2 To LastRow
Dim rFind As range
Set rFind = wsAMP.Columns(2).Find(what:=wsAN.Cells(i, 2), LookIn:=xlValues, LookAt:=xlPart, MatchCase:=True)
If Not rFind Is Nothing Then
wsAN.Cells(i, 5).Value = rFind.Offset(0, 10).Value
End If
Next i
End Sub
Basically, I would like to use my existing code shown in step 4, but with column Names (rather than column numbers). The names I would like to use are Name and ID from the AMP Sheet.
The .Offset logic in my code is the tricky part, since it's going from column B to Column L in the AMP Sheet, so it count column L as 10, rather than 12.
Thanks!
Find Headers and Lookup Values Using Application.Match
Option Explicit
Sub AMPTabid()
' Source
Const sName As String = "AMP Sheet"
Const slTitle As String = "Name"
Const svTitle As String = "ID"
' Destination
Const dName As String = "AssetName Sheet"
Const dlTitle As String = "Generated Asset Name"
Const dvTitle As String = "AMP ID"
' Workbook
Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing this code
' Source
Dim sws As Worksheet: Set sws = wb.Worksheets(sName)
Dim srg As Range: Set srg = sws.Range("A1").CurrentRegion ' Table Range
Dim shrg As Range: Set shrg = srg.Rows(1) ' Header Range
' Get the column numbers.
Dim slCol As Variant: slCol = Application.Match(slTitle, shrg, 0) ' Lookup
Dim svCol As Variant: svCol = Application.Match(svTitle, shrg, 0) ' Value
' Reference the data ranges.
Dim srCount As Long: srCount = srg.Rows.Count - 1
Dim sdrg As Range: Set sdrg = srg.Resize(srCount).Offset(1) ' Data Range
' Lookup Column Range ('Application.Match' works faster with ranges)
Dim slrg As Range: Set slrg = sdrg.Columns(slCol) ' lookup stays in range
Dim svrg As Range: Set svrg = sdrg.Columns(svCol) ' Value Range
Dim svData As Variant: svData = svrg.Value ' value data to array
' Destination
Dim dws As Worksheet: Set dws = wb.Worksheets(dName)
Dim drg As Range: Set drg = dws.Range("A1").CurrentRegion ' Table Range
Dim dhrg As Range: Set dhrg = drg.Rows(1) ' Header Range
' Get the column numbers.
Dim dlCol As Variant: dlCol = Application.Match(dlTitle, dhrg, 0) ' Lookup
Dim dvCol As Variant: dvCol = Application.Match(dvTitle, dhrg, 0) ' Value
' Reference the data ranges.
Dim drCount As Long: drCount = drg.Rows.Count - 1
Dim ddrg As Range: Set ddrg = drg.Resize(drCount).Offset(1) ' Data Range
Dim dlrg As Range: Set dlrg = ddrg.Columns(dlCol) ' Lookup Range
' The same array will be used for lookup and values (results).
Dim dData As Variant: dData = dlrg.Value ' lookup data to array
Dim dvrg As Range: Set dvrg = ddrg.Columns(dvCol) ' to be written to
Dim sIndex As Variant ' Source Lookup (and Value) Index
Dim dlValue As Variant ' Destination Lookup Value
Dim dr As Long
For dr = 1 To drCount
dlValue = dData(dr, 1)
If Not IsError(dlValue) Then ' exclude error values
If Len(dlValue) > 0 Then ' exclude blanks
sIndex = Application.Match(dlValue, slrg, 0)
If IsNumeric(sIndex) Then ' match found
dData(dr, 1) = svData(sIndex, 1)
Else ' no match found
dData(dr, 1) = Empty
End If
End If
End If
Next dr
dvrg.Value = dData
MsgBox "Ids updated.", vbInformation
End Sub
Related
I am trying to extract data from different sheets in a summary sheet.
The referencing does not work.
Sub Summary_LPI()
Dim wkSht As Worksheet, wsC As Worksheet, rngSearch As Range
Dim shNCell As Range
Set wsC = Sheets("Summary")
Set rngSearch = wsC.Range("A2:A60")
For Each wkSht In ThisWorkbook.Worksheets
'find the sheet name cell in rngSearch:
Set shNCell = rngSearch.Find(what:=wkSht.Name, LookIn:=xlValues, Lookat:=xlWhole,
MatchCase:=False)
'if found:
If Not shNCell Is Nothing Then
'copy the below built array in the necessary place
wkSht.Range("AZ56").Value = wsC.Range(shNCell.Offset(0, 6), shNCell.Offset(1, 6)).Value
End If
Next wkSht
End Sub
Copy Data Into a Summary Worksheet
Adjust the values in the constants section.
The order of the columns in the Summary worksheet needs to be the same as in each individual worksheet.
The number of columns to be pulled is defined by the last non-empty column in the first (header) row of the Summary worksheet.
Option Explicit
Sub Summary_LPI()
' s - Source, d - Destination
Const sfvCol As String = "AY" ' First Value Column
Const dName As String = "Summary"
Const dlCol As String = "A" ' Lookup Column
Const dfvColString As String = "F" ' First Value Column
Const dhRow As Long = 1 ' Header Row
Dim wb As Workbook: Set wb = ThisWorkbook
Dim dws As Worksheet: Set dws = wb.Worksheets(dName)
Dim dfRow As Long: dfRow = dhRow + 1 ' First Row
Dim dlrow As Long ' Last Row
dlrow = dws.Cells(dws.Rows.Count, dlCol).End(xlUp).Row
If dlrow < dfRow Then Exit Sub ' no data
Dim dlcrg As Range ' Lookup Column Range
Set dlcrg = dws.Range(dws.Cells(dfRow, dlCol), dws.Cells(dlrow, dlCol))
Dim dfvCol As Long: dfvCol = dws.Columns(dfvColString).Column
Dim dlvCol As Long ' Last Value Column
dlvCol = dws.Cells(dhRow, dws.Columns.Count).End(xlToLeft).Column
If dlvCol < dfvCol Then Exit Sub ' no data
Dim vcCount As Long: vcCount = dlvCol - dfvCol + 1 ' Value Columns Count
Application.ScreenUpdating = False
Dim sws As Worksheet
Dim svrrg As Range ' Value Row Range
Dim svRow As Long ' Value Row
Dim dvrrg As Range ' Value Row Range
Dim dlCell As Range ' Lookup Cell
For Each dlCell In dlcrg.Cells
Set dvrrg = dlCell.EntireRow.Columns(dfvCol).Resize(, vcCount)
On Error Resume Next
Set sws = wb.Worksheets(CStr(dlCell.Value))
On Error GoTo 0
If sws Is Nothing Then ' worksheet doesn't exist
dvrrg.ClearContents ' remove if you want to keep the previous
Else ' worksheet exists
svRow = sws.Cells(sws.Rows.Count, sfvCol).End(xlUp).Row
Set svrrg = sws.Cells(svRow, sfvCol).Resize(, vcCount)
dvrrg.Value = svrrg.Value
Set sws = Nothing
End If
Next dlCell
Application.ScreenUpdating = True
MsgBox "Summary updated."
End Sub
I have two sheets in my excel file:
Input Sheet: Sheet1
Target Sheet: Sheet2
What I want to achieve is to paste the value start from the column that I defined in cell C5 and also start from the row that I defined in cell C6. If the range defined by cell C5 and C6 already have data, then it will find the next empty row based on the column in cell C5 and paste the data in that empty row.
For example in the screenshot above, the starting column & row defined in cell C5 & C6 is B8, so the copied value will be pasted starting from cell B8 until E8. However, if the row already have data, then it will find the next empty row based on column B (which is B9) and paste it there.
I'm not sure how to modified my current script:
Public Sub CopyData()
Dim InputSheet As Worksheet ' set data input sheet
Set InputSheet = ThisWorkbook.Worksheets("Sheet1")
Dim InputRange As Range ' define input range
Set InputRange = InputSheet.Range("G6:J106")
Dim TargetSheet As Worksheet
Set TargetSheet = ThisWorkbook.Worksheets("Sheet2")
Const TargetStartCol As Long = 2 ' start pasting in this column in target sheet
Const PrimaryKeyCol As Long = 1 ' this is the unique primary key in the input range (means first column of B6:G6 is primary key)
Dim InsertRow As Long
InsertRow = TargetSheet.Cells(TargetSheet.Rows.Count, TargetStartCol + PrimaryKeyCol - 1).End(xlUp).Row + 1
' copy values to target row
TargetSheet.Cells(InsertRow, TargetStartCol).Resize(ColumnSize:=InputRange.Columns.Count).Value = InputRange.Value
End Sub
Any help or advice will be greatly appreciated!
Testing Scenario 1
Output of Testing Scenario 1
Please, try the next code:
Public Sub CopyData_()
Dim InputSheet As Worksheet: Set InputSheet = ThisWorkbook.Worksheets("Sheet1")
Dim InputRange As Range: Set InputRange = InputSheet.Range("G6:J106")
Dim arr: arr = InputRange.Value
Dim TargetSheet As Worksheet: Set TargetSheet = ThisWorkbook.Worksheets("Sheet2")
Dim TargetStartCol As String, PrimaryKeyRow As Long
TargetStartCol = TargetSheet.Range("C5").Value ' start pasting in this column in target sheet
PrimaryKeyRow = TargetSheet.Range("C6").Value ' this is the row after the result to be copied
Dim InsertRow As Long
InsertRow = TargetSheet.cells(TargetSheet.rows.Count, TargetStartCol).End(xlUp).row + 1
If InsertRow < PrimaryKeyRow Then InsertRow = PrimaryKeyRow + 1 'in case of no entry after PrimaryKeyRow (neither the label you show: "Row")
' copy values to target row
TargetSheet.cells(InsertRow, TargetStartCol).Resize(UBound(arr), UBound(arr, 2)).Value = arr
End Sub
Not tested, but if should work, I think. If something not clear or going wrong, please do not hesitate to mention the error, what it does/doesn't against you need or anything else, necessary to correct it.
Copy Data to Another Worksheet
Option Explicit
Sub CopyData()
Const sName As String = "Sheet1"
Const rgAddress As String = "G6:J106"
Dim wb As Workbook: Set wb = ThisWorkbook
Dim ws As Worksheet: Set ws = wb.Worksheets(sName)
Dim rg As Range: Set rg = ws.Range(rgAddress)
WriteCopyData rg
' or just:
'WriteCopyData ThisWorkbook.Worksheets("Sheet1").Range("G6:J106")
End Sub
Sub WriteCopyData(ByVal SourceRange As Range)
Const dName As String = "Sheet2"
Const dRowAddress As String = "C6"
Const dColumnAddress As String = "C5"
Dim rCount As Long: rCount = SourceRange.Rows.Count
Dim cCount As Long: cCount = SourceRange.Columns.Count
Dim dws As Worksheet
Set dws = SourceRange.Worksheet.Parent.Worksheets(dName)
Dim dRow As Long: dRow = dws.Range(dRowAddress).Value
Dim dCol As String: dCol = dws.Range(dColumnAddress).Value
Dim dfrrg As Range: Set dfrrg = dws.Cells(dRow, dCol).Resize(1, cCount)
Dim dlCell As Range
Set dlCell = dfrrg.Resize(dws.Rows.Count - dRow + 1) _
.Find("*", , xlFormulas, , xlByRows, xlPrevious)
If Not dlCell Is Nothing Then
Set dfrrg = dfrrg.Offset(dlCell.Row - dRow + 1)
End If
Dim drg As Range: Set drg = dfrrg.Resize(rCount)
drg.Value = SourceRange.Value
End Sub
I have range of years from 1994-2014 and for a reach corresponding company names values lies against each other (Output Sheet). There are sales figure for the respective company for each year which I used this formula (below) to get from the Sheet1 to output sheet.
Source Sheet/Sheet1
Output Sheet
=INDEX('Sheet1'!$E$5:$Y$685,MATCH(Output!B2,'Sheet1'!$D$5:$D$685,0),MATCH(Output!A2,'Sheet1'!$E$4:$Y$4,0))
I used two match formula as I wanted to validate company name as well as the year.
NOW, I want to check the values I retrieved from the above equation is an exact match/True to the source value. Thus, I tried using this formula but although the first IF logical is true, the second fails.
=IFS(Output!B2=INDEX('Sheet1'!$D$5:$D$685,MATCH(Output!B2,'Sheet1'!$D$5:$D$685,0)),"OK",C2=INDEX('Sheet1'!$E$5:$Y$685,MATCH(Output!B2,'Sheet1'!$D$5:$D$685,0),MATCH(Output!A2,'Sheet1'!$E$4:$Y$4,0)),"FINE")
I am looking for VBA code for the entire task at hand in case VBA makes it easier as I have huge dataset to perform the same procedure.
A VBA Unpivot
Copy the code into a standard module, e.g. Module1 of the workbook containing the two worksheets.
Carefully adjust the values in the constants section.
Both cell addresses refer to the first cells of the table headers.
You should give PowerQuery a try. It will take a few minutes once you get a hang of it. And it has a ton of options.
Option Explicit
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Purpose: Unpivots a table range (has headers) to another worksheet.
' Calls: 'RefCurrentRegionBottomRight'.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Sub UnPivotData()
' Source
Const sName As String = "Sheet1"
Const sFirstCellAddress As String = "D4"
Const scCount As Long = 22
' Destination
Const dName As String = "Output"
Const dFirstCellAddress As String = "A1"
Dim dHeaders As Variant: dHeaders = VBA.Array("YEAR", "COMPANY", "WC01651")
' Workbook
Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing this code
' Write from source range to source array.
Dim sws As Worksheet: Set sws = wb.Worksheets(sName)
Dim sfCell As Range: Set sfCell = sws.Range(sFirstCellAddress)
Dim srg As Range
Set srg = RefCurrentRegionBottomRight(sfCell).Resize(, scCount)
Dim srCount As Long: srCount = srg.Rows.Count
Dim sData As Variant: sData = srg.Value
' Size destination array.
Dim dhUpper As Long: dhUpper = UBound(dHeaders)
Dim drCount As Long: drCount = (srCount - 1) * (scCount - 1) + 1
Dim dcCount As Long: dcCount = dhUpper + 1 ' zero- vs one-based
Dim dData As Variant: ReDim dData(1 To drCount, 1 To dcCount)
' Write headers.
Dim dh As Long
For dh = 0 To dhUpper
dData(1, dh + 1) = dHeaders(dh)
Next dh
Dim dr As Long: dr = 1 ' headers already written
Dim sr As Long
Dim sc As Long
' Write data ('body').
For sr = 2 To srCount
For sc = 2 To scCount
dr = dr + 1 ' Note the 'PowerQuery' terms in parentheses:
dData(dr, 1) = sData(1, sc) ' write column labels (attributes)
dData(dr, 2) = sData(sr, 1) ' write row labels
dData(dr, 3) = sData(sr, sc) ' write values (values)
Next sc
Next sr
' Write from destination array to destination range.
Dim dws As Worksheet: Set dws = wb.Worksheets(dName)
Dim dfCell As Range: Set dfCell = dws.Range(dFirstCellAddress)
Dim dcrg As Range
Set dcrg = dfCell.Resize(dws.Rows.Count - dfCell.Row + 1, dcCount)
dcrg.ClearContents
Dim drg As Range: Set drg = dfCell.Resize(drCount, dcCount)
drg.Value = dData
MsgBox "Data transferred.", vbInformation
End Sub
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Purpose: Returns a reference to the range starting with a given cell
' and ending with the last cell of its Current Region.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function RefCurrentRegionBottomRight( _
ByVal FirstCellRange As Range) _
As Range
If FirstCellRange Is Nothing Then Exit Function
With FirstCellRange.Cells(1).CurrentRegion
Set RefCurrentRegionBottomRight = _
FirstCellRange.Resize(.Row + .Rows.Count - FirstCellRange.Row, _
.Column + .Columns.Count - FirstCellRange.Column)
End With
End Function
So I have this problem where if there is a value in a column, the row should be duplicated and copied to the next sheet. I will show a scenario to understand better.
This is sheet1
As you can see from the table above, there is a certain item name that doesn't have the three quantity columns. Some only have good quantity, some have both good and bad, and some have the three quantity. Now I want to copy this data to the other sheet with some modifications.
This should be the result in the next sheet:
As you can see, the data are duplicated based on the quantity columns if there is data or not. The status column is based on the quantity columns in sheet1. Status 0 is GOOD QTY, Status 1 is BAD QTY and Status 2 is VERY BAD QTY. This is my current code:
Set countsheet = ThisWorkbook.Sheets("Sheet1")
Set uploadsheet = ThisWorkbook.Sheets("Sheet2")
countsheet.Activate
countsheet.Range("B11", Range("F" & Rows.Count).End(xlUp)).Select
Selection.SpecialCells(xlCellTypeVisible).Select
Selection.Copy
uploadsheet.Activate
uploadsheet.Range("B2").PasteSpecial xlPasteValues
I know this code only copies data from sheet1 to sheet2. How to modify this code and achieve the result above?
VBA Unpivot
Option Explicit
Sub UnpivotData()
' Needs the 'RefColumn' function.
' Source
Const sName As String = "Sheet1"
Const sFirstCellAddress As String = "B11" ' also Unique Column First Cell
Const sAddCount = 1 ' Additional Column i.e. 'ITEM NAME'
Const sAttrTitle As String = "STATUS"
Const sAttrRepsList As String = "0,1,2" ' Attribute Replacements List
Const sValueTitleAddress As String = "D10" ' i.e. QTY
' Destination
Const dName As String = "Sheet2"
Const dFirstCellAddress As String = "B2"
' Workbook
Dim wb As Workbook: Set wb = ThisWorkbook
' Reference the first column range.
Dim sws As Worksheet: Set sws = wb.Worksheets(sName)
Dim sfCell As Range: Set sfCell = sws.Range(sFirstCellAddress)
Dim sfcrg As Range: Set sfcrg = RefColumn(sfCell)
If sfcrg Is Nothing Then Exit Sub ' no data in the first (unique) column
' Reference the range and write it to an array.
Dim sAttrReps() As String: sAttrReps = Split(sAttrRepsList, ",")
Dim sAttrCount As Long: sAttrCount = UBound(sAttrReps) + 1
Dim scUniqueCount As Long: scUniqueCount = 1 + sAddCount
Dim scCount As Long: scCount = scUniqueCount + sAttrCount
Dim srg As Range: Set srg = sfcrg.Resize(, scCount)
Dim sData As Variant: sData = srg.Value
' Determine the destination size.
Dim srCount As Long: srCount = srg.Rows.Count
Dim svrg As Range
Set svrg = srg.Resize(srCount - 1, sAttrCount) _
.Offset(1, scUniqueCount)
Dim drCount As Long: drCount = Application.Count(svrg) + 1
Dim dcCount As Long: dcCount = scUniqueCount + 2
Dim dData As Variant: ReDim dData(1 To drCount, 1 To dcCount)
' Write the title row to the destination array.
Dim scu As Long ' Unique Columns
For scu = 1 To scUniqueCount
dData(1, scu) = sData(1, scu) ' Unique
Next scu
dData(1, scu) = sAttrTitle ' Attributes
dData(1, scu + 1) = sws.Range(sValueTitleAddress).Value ' Values
' Write the data rows to the destination array.
Dim dr As Long: dr = 1 ' first row already written
Dim sr As Long ' Rows
Dim sca As Long ' Attribute Columns
For sr = 2 To srCount ' first row already written
For sca = 1 To sAttrCount
If Len(CStr(sData(sr, sca + scUniqueCount))) > 0 Then
dr = dr + 1
For scu = 1 To scUniqueCount
dData(dr, scu) = sData(sr, scu) ' Unique
Next scu
dData(dr, scu) = sAttrReps(sca - 1) ' Attributes
dData(dr, scu + 1) = sData(sr, sca + scUniqueCount) ' Values
End If
Next sca
Next sr
' Write the destination array to the destination range.
Dim dws As Worksheet: Set dws = wb.Worksheets(dName)
Dim dfCell As Range: Set dfCell = dws.Range(dFirstCellAddress)
Dim drg As Range: Set drg = dfCell.Resize(drCount, dcCount)
drg.Value = dData
' Clear below the destination range.
With drg
Dim dcrg As Range
Set dcrg = .Resize(dws.Rows.Count - .Row - drCount + 1).Offset(drCount)
dcrg.Clear ' possibly just 'dcrg.ClearContents'
End With
MsgBox "Unpivot successful.", vbInformation, "Unpivot Data"
End Sub
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Purpose: Creates a reference to the one-column range from the first cell
' of a range ('FirstCell') to the bottom-most non-empty cell
' of the first cell's worksheet column.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function RefColumn( _
ByVal FirstCell As Range) _
As Range
If FirstCell Is Nothing Then Exit Function
With FirstCell.Cells(1)
Dim lCell As Range
Set lCell = .Resize(.Worksheet.Rows.Count - .Row + 1) _
.Find("*", , xlFormulas, , , xlPrevious)
If lCell Is Nothing Then Exit Function
Set RefColumn = .Resize(lCell.Row - .Row + 1)
End With
End Function
Still learning the ropes so bear with! I have a monthly data dump that will be copied into the workbook, it is always in the same format. I'm trying to write a macro that filters the data in a preset column using a list of names from another sheet within the workbook. Ideally I want to be able to add or remove names from the list. Once it has filtered I'd like it to copy all those visible cells and paste them into a new sheet.
I've started with using the autofilter and then a counting array, but I am getting an error AND it's not filtering. In that the filter is applied to the sheet, but it doesn't seem to be able to look for the actual names, and just returns blanks.
It does seem to count the right number of names in my dynamic list... so I'll take that.
So example data:
Worksheet: Names
Worksheet: Books
Code ideally takes the list of names from the Person column in "Names", looks through the Name column "Books", finds each match and then copies and dumps the entire row to a new sheet.
Here is my best attempt at writing something.
Sub FilterName()
Dim i As Long
Dim lastrow As Long
Dim arrSummary() As Variant
With ThisWorkbook.Sheets("Names")
lastrow = .Cells(Rows.Count, "B").End(xlUp).Row
ReDim arrSummary(1 To lastrow)
For i = 1 To lastrow
arrSummary(i) = .Cells(i, 1)
Next
End With
For i = LBound(arrSummary) To UBound(arrSummary)
With ThisWorkbook.Sheets("Books")
.Range("F:F").AutoFilter Field:=1, Criteria1:=arrSummary(i), Operator:=xlFilterValues
.ThisWorkbook.Sheets("Books").Range("A1:AA100000").SpecialCells(xlCellTypeVisible).Copy
'Getting error 438 here
.ThisWorkbook.Sheets("Loans").Paste
End With
Next i
End Sub
I did contemplate advanced filter but couldn't make that work even outside of VBA, and then didn't want to do the find route as felt it was clunky...Willing to explore these options though.
Cheers :)
Filter Names
It will write the values from column B (cCol) of the criteria worksheet (cws) to a 2D one-based one-column array (cData). Then it will loop through the values in the array and filter the 6th column (scCol) of the source worksheet (sws) by each of the array's values and copy the source range's (A:AA) rows that contain the matching cells to the first available row of the destination worksheet (dws) starting in column A (dfCol).
Option Explicit
Sub FilterNames()
' Criteria
Const cName As String = "Names"
Const cCol As String = "B"
Const cfRow As Long = 2
' Source
Const sName As String = "Books"
Const sCols As String = "A:AA"
Const scCol As Long = 6 ' also used for AutoFilter's Field parameter
Const sfRow As Long = 1
' Destination
Const dName As String = "Loans"
Const dfCol As String = "A"
Const dfRow As Long = 2
' Workbook
Dim wb As Workbook: Set wb = ThisWorkbook
' Criteria
Dim cws As Worksheet: Set cws = wb.Worksheets(cName)
Dim clRow As Long: clRow = cws.Cells(cws.Rows.Count, cCol).End(xlUp).Row
If clRow < cfRow Then Exit Sub
Dim crCount As Long: crCount = clRow - cfRow + 1
Dim crg As Range: Set crg = cws.Cells(cfRow, cCol).Resize(crCount)
Dim cData As Variant
If crCount = 1 Then
ReDim cData(1 To 1, 1 To 1): cData(1, 1) = crg.Value
Else
cData = crg.Value
End If
' Source
Dim sws As Worksheet: Set sws = wb.Worksheets(sName)
Dim srg As Range: Set srg = sws.UsedRange.Columns(sCols)
Dim sdrg As Range: Set sdrg = srg.Resize(srg.Rows.Count - 1).Offset(1)
Dim sdcrg As Range: Set sdcrg = sdrg.Columns(scCol)
' Destination
Dim dws As Worksheet: Set dws = wb.Worksheets(dName)
Dim dlRow As Long: dlRow = dws.Cells(dws.Rows.Count, dfCol).End(xlUp).Row
Dim dCell As Range
If dlRow < dfRow Then
Set dCell = dws.Cells(dfRow, dfCol)
Else
Set dCell = dws.Cells(dlRow, dfCol).Offset(1)
End If
Application.ScreenUpdating = False
Dim drCount As Long
Dim r As Long
For r = 1 To UBound(cData, 1)
sws.AutoFilterMode = False
srg.AutoFilter scCol, CStr(cData(r, 1)), xlFilterValues
drCount = Application.Subtotal(103, sdcrg)
Debug.Print drCount, cData(r, 1)
If drCount > 0 Then
sdrg.SpecialCells(xlCellTypeVisible).Copy
dCell.PasteSpecial xlPasteValues
Set dCell = dCell.Offset(drCount)
End If
Next r
Application.CutCopyMode = False
sws.AutoFilterMode = False
If dws Is ActiveSheet Then
dws.Range("A1").Activate
Else
Dim ash As Worksheet: Set ash = ActiveSheet
dws.Activate
dws.Range("A1").Activate
ash.Activate
End If
'wb.Save
Application.ScreenUpdating = True
MsgBox "Data transferred.", vbInformation, "Filter Names"
End Sub
You can achieve your goal without VBA but with the new FILTER-function if you have Excel 365.
In my example I created two tables (Insert > Table) named them tblPeople and tblBooks.
That way the formula is very easy to read:
Regarding your code: When you have a lot of data this process will be very slow.
In general you achieve a better performance when reading the data into an array (like you already did with the peoples sheet), do the filtering in the array and then write the array back to the sheet (you will find a lot of examples here on SO.
By the way: you can read a range to an array like this:
arrSummary = rg.value where rg is the range you want to read.