VBA Excel: Paste into Matching Row between Workbooks - excel

I am trying to develop code to do the following:
1)Copy cells K4: M4 in Workbook 1, Sheet 1 <- I can do this step;
2)Find a cell in Workbook2, Sheet1, column C that matches cell B4 in
Workbook1, Sheet1;
3)Paste the copied values in columns P:R of the matching row in
Workbook 2, Sheet 1 as determined in Step 2.
My apologies in advance for being unable to advance my own work beyond step 1. I am, as I said, completely new to this and have scoured the web for an answer/learnings up until this point, without turning up a solution.

I tested this and it worked. Does this help you get started?
Sub CopyToMatchedRow()
Dim copyRng As Range, matchVal As Variant, matchRng As Range, matchRow As Integer
Set copyRng = Worksheets("Sheet1").Range("K4:M4")
Set matchRng = Worksheets("Sheet2").Range("C:C")
matchVal = Worksheets("Sheet1").Range("B4")
matchRow = matchRng.Find(What:=matchVal, After:=ActiveCell, LookIn:=xlFormulas, LookAt:=xlPart, SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:=False, SearchFormat:=False)
copyRng.Copy Destination:=Worksheets("Sheet2").Range("P" & matchRow & ":R" & matchRow)
End Sub

Related

Using Lookup function in VBA

I am currently automating a sales report with VBA and I am having trouble with inserting a formula using VBA with a dynamic range. My Formula with Lookup finds the last week that a client ordered.
The current week is the last week and is always the column prior to the Total column. I am having trouble referencing that last column with the current week.
=LOOKUP(2,1/(CL[#[Week 1]:[Week 17]]>0),COLUMN(CL[[#Headers],[Week 1]:[Week 17]]))
I wasn't sure how to reference using headers.
So the first part of my code finds the column number and using the column number I get the Letter reference. Not sure how to use the Letters with my LOOKUP formula
Sub LastOrder()
Dim strSearch As String
Dim strSearchEnd As String
Dim aCell As Range
Dim endCell As Range
Dim startingCol As Variant
Dim endingCol As Variant
Dim colFirstWeek As Variant
Dim ColLastWeek As Variant
Dim firstCheck As Variant
Dim lastCheck As Variant
'find the column number for week 1 and total
strSearch = "Week 1"
strSearchEnd = "Total"
Set aCell = Sheet1.Rows(1).Find(What:=strSearch, LookIn:=xlValues, _
LookAt:=xlWhole, SearchOrder:=xlByRows, SearchDirection:=xlNext, _
MatchCase:=False, SearchFormat:=False)
If Not aCell Is Nothing Then
startingCol = aCell.Column
End If
Set endCell = Sheet1.Rows(1).Find(What:=strSearchEnd, LookIn:=xlValues, _
LookAt:=xlWhole, SearchOrder:=xlByRows, SearchDirection:=xlNext, _
MatchCase:=False, SearchFormat:=False)
If Not endCell Is Nothing Then
endingCol = endCell.Column - 1
'this is used to get column number of current week
End If
'Use letter reference
firstCheck = Split(Cells(, startingCol).Address, "$")(1)
lastCheck = Split(Cells(, endingCol).Address, "$")(1)
Debug.Print (firstCheck)
Debug.Print (lastCheck)
Range("CL[Last Week Ordered]").FormulaR1C1 = _
"LOOKUP(2,1/(firstCheck:lastCheck>0),COLUMN(firstCheck:lastCheck))
You can try this formula :
=LOOKUP(2, 1 / ( [#[Week 1]]:INDEX([#], , COLUMNS([#]) - 1) > 0 ),
COLUMN( [#[Week 1]]:INDEX([#], , COLUMNS([#]) - 1) ) )
Few Excel Functions like INDEX and OFFSET return Range reference, so they can be used with the range operator :, and Range("INDEX(A1:B1, 1, 1)") in VBA.

Copy and paste selected dynamic data columns from one sheet to another sheet

I want to copy data from specific column in Sheet 1 to a specific column in Sheet 2. There are 20 such columns and that mapping is maintained in a table like
I have written the code to search column name (source and destination sheets) from but am unable to copy the data from source column (dynamic range) to destination column.
Sub search_validate()
Dim j As Integer
Dim sourcSearch, destSearch As String
Dim sCell, dCell As Range
For j = 3 To 20
sourcSearch = Sheet6.Range("Z" & j).Value ' pickup selected source column name
destSearch = Sheet6.Range("AA" & j).Value ' pickup selected destination column name
Set sCell = Sheet1.Rows(2).Find(What:=sourcSearch, LookIn:=xlValues, _
LookAt:=xlWhole, SearchOrder:=xlByRows, SearchDirection:=xlNext, _
MatchCase:=False, SearchFormat:=False)
Set dCell = Sheet2.Rows(2).Find(What:=destSearch, LookIn:=xlValues, _
LookAt:=xlWhole, SearchOrder:=xlByRows, SearchDirection:=xlNext, _
MatchCase:=False, SearchFormat:=False)
' sCell.Address or sCell.Column to get source column header address but data starts after this column. dynamic range
' dCell.Address or dCell.Column to get destination column header address. no data in destination column except header.
Next j
End Sub
This should append the data to the end of the destination column
If Not sCell Is Nothing And Not dCell Is Nothing Then
Dim Source As Range, Target As Range
Set Source = Intersect(Sheet1.UsedRange, sCell.EntireColumn).Offset(1)
Set Target = Sheet2.Cells(Sheet2.Rows.Count, dCell.Column).End(xlUp).Offset(1)
Source.Copy Destination:=Target
End If
You need to paste new data after last row in your current table?
U can find last row with this:
lastRow = Columns("Enter your column number here").Find("*", SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row
Then just make a copy-paste, like this:
Set copyRange = Range(YOUR RANGE HERE)
copyRange.Copy
Cells(lastRow + 1, "Enter your column number here").PasteSpecial Paste:=xlPasteValues

Is it possible to specify dynamic range in auto filter syntax?

I have written below code to apply auto filter. Code is working fine. However I have specified static range, here: *Range("A1", **"M"** & lastRow).AutoFilter* .
Is there a way to replace "M" with last existing column number in sheet2.
I have calculated last existing column *myCol = rngFound.Column - 1*. But not sure how to use it.
Please help !!!
My Code:
Sub testfilter1()
Dim lastRow As Long
Dim myCol As Long
Dim rngFound As Range
ThisWorkbook.Sheets("sheet2").Activate
lastRow = ActiveSheet.Range("A" & Rows.Count).End(xlUp).Row
Set rngFound = ActiveSheet.Rows(1).Find(What:="", LookIn:=xlValues, LookAt:=xlWhole, _
SearchOrder:=xlByColumns, SearchDirection:=xlNext, MatchCase:=False)
myCol = rngFound.Column - 1 ' this will give last used column
Rows("1:1").Select
Selection.AutoFilter
ActiveSheet.Range("A1", "M" & lastRow).AutoFilter Field:=4, Criteria1:="*somename*"
End Sub
You could use Cells() within Range():
...Range(Cells(1,1),Cells(lastRow,myCol))...
Cells(1,1) is the cell A1. The format is Cells([row],[column])
Edit: Just realized you could also use Range("A1",Cells(lastRow,myCol)). Personally, if I use Cells() I do it in both places in Range(), but that's personal preference and this other way should work for you too.

Countering circular reference with SUM and OFFSET (VLOOKUP & VBA involved)

To give you a breakdown of my spreadsheet:
I have a master spreadsheet that pulls data from another spreadsheet (generated daily), placing it into the next empty column and converting the column that previously held the formula to values. This is achieved with a combination of the following formula and VBA code:
=IF(ISNA(VLOOKUP("Row 1",'N:\Reports\[data.xls]Sheet1'!$A$2:$B$40,2,FALSE)),0,(VLOOKUP("Row 1",'N:\Reports\[data.xls]Sheet1'!$A$2:$B$40,2,FALSE)))
Sub Test()
Dim ws As Worksheet
Set ws = ActiveSheet
Dim rLastCell As Range
Dim LastCol As Integer
Set rLastCell = ws.Cells.Find(what:="*", After:=ws.Cells(1, 1), LookIn:=xlFormulas, LookAt:= _
xlPart, SearchOrder:=xlByColumns, SearchDirection:=xlPrevious, MatchCase:=False)
LastCol = rLastCell.Column
ws.Columns(LastCol).Copy ws.Columns(LastCol + 1)
With ws.Columns(LastCol)
.Copy .Offset(0, 1)
.Value = .Value
End With
End Sub
The intention is for Column B to be a 'totals' column, that dynamically sums all of the values in the relevant row as new entries are pulled by the formula/VBA combo and added to the first blank column. Unfortunately though, I also need to subtract that row's total from the value that the formula returns--however, doing so creates a circular reference.
My solution was to just exclude the last cell in the row (that has the formula) from the total, with this:
=SUM(C2:OFFSET(I$2,0,-1))
However, the dynamic range doesn't appear all that dynamic. It doesn't expand to include the next column when a new record is added, and I'm really not enough of a hand at this stuff to figure out why or how to rectify it.
Thanks in advance for any assistance with this and please don't hesitate to ask for any clarification!
It may be simplest to use a named range. If you name the column before the monthly total say LastDay, you can use:
=SUM(C2:INDEX(LastDay,ROW())
as your formula, and your code then becomes:
Sub Test()
Dim ws As Worksheet
Set ws = ActiveSheet
Dim rLastCell As Range
Dim LastCol As Integer
Set rLastCell = ws.Cells.Find(what:="*", After:=ws.Cells(1, 1), LookIn:=xlFormulas, LookAt:= _
xlPart, SearchOrder:=xlByColumns, SearchDirection:=xlPrevious, MatchCase:=False)
LastCol = rLastCell.Column
With ws.Columns(LastCol)
.Copy .Offset(0, 1)
.Value = .Value
.Name = "LastDay"
End With
End Sub
Assuming I have understood your layout correctly.

How do I find the last column with data?

I've found this method for finding the last data containing row in a sheet:
ws.Range("A65536").End(xlUp).row
Is there a similar method for finding the last data containing column in a sheet?
Lots of ways to do this. The most reliable is find.
Dim rLastCell As Range
Set rLastCell = ws.Cells.Find(What:="*", After:=ws.Cells(1, 1), LookIn:=xlFormulas, LookAt:= _
xlPart, SearchOrder:=xlByColumns, SearchDirection:=xlPrevious, MatchCase:=False)
MsgBox ("The last used column is: " & rLastCell.Column)
If you want to find the last column used in a particular row you can use:
Dim lColumn As Long
lColumn = ws.Cells(1, Columns.Count).End(xlToLeft).Column
Using used range (less reliable):
Dim lColumn As Long
lColumn = ws.UsedRange.Columns.Count
Using used range wont work if you have no data in column A. See here for another issue with used range:
See Here regarding resetting used range.
I know this is old, but I've tested this in many ways and it hasn't let me down yet, unless someone can tell me otherwise.
Row number
Row = ws.Cells.Find(What:="*", After:=[A1] , SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row
Column Letter
ColumnLetter = Split(ws.Cells.Find(What:="*", After:=[A1], SearchOrder:=xlByColumns, SearchDirection:=xlPrevious).Cells.Address(1, 0), "$")(0)
Column Number
ColumnNumber = ws.Cells.Find(What:="*", After:=[A1], SearchOrder:=xlByColumns, SearchDirection:=xlPrevious).Column
Try using the code after you active the sheet:
Dim J as integer
J = ActiveSheet.UsedRange.SpecialCells(xlCellTypeLastCell).Row
If you use Cells.SpecialCells(xlCellTypeLastCell).Row only, the problem will be that the xlCellTypeLastCell information will not be updated unless one do a "Save file" action. But use UsedRange will always update the information in realtime.
I think we can modify the UsedRange code from #Readify's answer above to get the last used column even if the starting columns are blank or not.
So this lColumn = ws.UsedRange.Columns.Count modified to
this lColumn = ws.UsedRange.Column + ws.UsedRange.Columns.Count - 1 will give reliable results always
?Sheet1.UsedRange.Column + Sheet1.UsedRange.Columns.Count - 1
Above line Yields 9 in the immediate window.
Here's something which might be useful. Selecting the entire column based on a row containing data, in this case i am using 5th row:
Dim lColumn As Long
lColumn = ActiveSheet.Cells(5, Columns.Count).End(xlToLeft).Column
MsgBox ("The last used column is: " & lColumn)
I have been using #Reafidy method/answer for a long time, but today I ran into an issue with the top row being merged cell from A1-->N1 and my function returning the "Last Column" as 1 not 14.
Here is my modified function now account for possibly merged cells:
Public Function Get_lRow(WS As Worksheet) As Integer
On Error Resume Next
If Not IsWorksheetEmpty(WS) Then
Get_lRow = WS.Cells.Find("*", SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row
Dim Cell As Range
For Each Cell In WS.UsedRange
If Cell.MergeCells Then
With Cell.MergeArea
If .Cells(.Cells.Count).Row > Get_lRow Then Get_lRow = .Cells(.Cells.Count).Row
End With
End If
Next Cell
Else
Get_lRow = 1
End If
End Function
Public Function Get_lCol(WS As Worksheet) As Integer
On Error Resume Next
If Not IsWorksheetEmpty(WS) Then
Get_lCol = WS.Cells.Find(What:="*", after:=[A1], SearchOrder:=xlByColumns, SearchDirection:=xlPrevious).Column
Dim Cell As Range
For Each Cell In WS.UsedRange
If Cell.MergeCells Then
With Cell.MergeArea
If .Cells(.Cells.Count).Column > Get_lCol Then Get_lCol = .Cells(.Cells.Count).Column
End With
End If
Next Cell
Else
Get_lCol = 1
End If
End Function
Here's a simple option if your data starts in the first row.
MsgBox "Last Row: " + CStr(Application.WorksheetFunction.CountA(ActiveSheet.Cells(1).EntireRow))
It just uses CountA to count the number of columns with data in the entire row.
This has all sorts of scenarios where it won't work, such as if you have multiple tables sharing the top row, but for a few quick & easy things it works perfect.

Resources