Excel VBA: Copy and paste selection based off row and column headers - vba

I am trying to create a macro that will format and create a weekly report.
It has five columns that need to be moved: Key, Summary, Created, Status, Fix Version/s.
I need to copy a selection that starts at Row 2 in the Key column and ends at the last row in the Fix Version/s column that will then be pasted into a sheet called "Priority Issues". I am unsure how to code this specific selection.
I need to store the last row in a variable, as the last row may change from week to week. In essence, I am looking for code that allows me to make a selection from the intersection of row 2 and the Key column to the intersection of the last row and the Fix Version/s column, but am unsure how to do that.
Function FindCol(toFind As String) As Range
Dim Rtn As Range
Set Rtn = Rows(1).Find(What:=toFind, LookIn:=xlValues, _
LookAt:=xlWhole, MatchCase:=True)
Set FindCol = Rtn
End Function
Sub Move_Severity()
Dim Severity As Range
Dim Key As Range
Dim Fix_Version As Range
Dim LastRow As Long
Set Severity = FindCol("Severity")
Set Key = FindCol("Key")
Set Fix_Version = FindCol("Fix Version/s")
LastRow = Cells(Rows.Count, Severity.Column).End(xlUp).Row
'This is where I am running into problems
Range(Cells(2, Key), Cells(LastRow, Fix_Version)).Copy
Sheets("Priority Issues").Range("A2").Paste
End Sub

Sub Move_Severity()
Dim Severity As Range
Dim Key As Range
Dim Fix_Version As Range
Dim LastRow As Long
Set Severity = FindCol("Severity")
Set Key = FindCol("Key")
Set Fix_Version = FindCol("Fix Version/s")
With ActiveSheet '<~~ change active sheet reference to whatever must it be
.Range(.Cells(2, Key.Column), .Cells(.Rows.Count, Severity.Column).End(xlUp)).Copy Destination:=Sheets("Priority Issues").Range("A2")
End With
End Sub
should you only need to paste values then change that With-End With part to
With ActiveSheet
With .Range(.Cells(2, Key.Column), .Cells(.Rows.Count, Severity.Column).End(xlUp))
Sheets("Priority Issues").Range("A2").Resize(.Rows.Count, .Columns.Count).Value = .Value
End With
End With
which is much faster

Related

Index match match/vlookup in VBA

I have an Excel document with two different Sheets. Sheet 2 has columns header names and rows header names. Sheet 1 has some of these columns with exact header names and rows header names but it's filled with data.
enter image description here, enter image description here
I want to make a macro that will look through all the column/rows headers in Sheet 1 and find their corresponding match in Sheet2. When the match is found, I need to copy the entry of the Sheet column/row header into the matching header of sheet2. Some entries in Sheet2 will not have matches and will remain blank.
I want it to look like this:
enter image description here
This is my code so far, it is working for the column headers but I don't know how to add for row headers as well. Any help is welcomed :)
Sub CopyData()
Application.ScreenUpdating = False
Dim LastRow As Long, header As Range, foundHeader As Range, lCol As Long, srcWS As Worksheet, desWS As Worksheet
Set srcWS = Sheets("Sheet1")
Set desWS = Sheets("Sheet2")
LastRow = srcWS.Cells.Find("*", SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row
lCol = desWS.Cells(3, Columns.Count).End(xlToLeft).Column
For Each header In desWS.Range(desWS.Cells(3, 2), desWS.Cells(3, lCol))
Set foundHeader = srcWS.Rows(2).Find(header, LookIn:=xlValues, lookat:=xlWhole)
If Not foundHeader Is Nothing Then
srcWS.Range(srcWS.Cells(3, foundHeader.Column), srcWS.Cells(LastRow, foundHeader.Column)).Copy desWS.Cells(4, header.Column)
End If
Next header
Application.ScreenUpdating = True
End Sub
You can use built-in Range.Consolidate method (https://learn.microsoft.com/en-us/office/vba/api/excel.range.consolidate):
(Edit2)
Option Explicit
Sub ConsolidateThis()
Dim rng1 As Range, rng2 As Range, addr As String
With ThisWorkbook
' determine source and destination ranges
Set rng1 = getTableRange(.Worksheets("Sheet1").Range("A2"))
Set rng2 = getTableRange(.Worksheets("Sheet2").Range("A3"))
' make full address of consolidated range like "'[Consolidate.xlsm]Sheet1'!R3C1:R6C5"
addr = "'[" & .Name & "]" & rng1.Parent.Name & "'!" & rng1.Address(ReferenceStyle:=xlR1C1)
' do consolidation
rng2.Consolidate Sources:=Array(addr), Function:=xlSum, TopRow:=True, LeftColumn:=True
End With
End Sub
' Returns the range that starts with the top left corner cell and is bounded
' on the right and bottom by empty cells
Function getTableRange(LeftTopCornerCell As Range) As Range
Dim ws As Worksheet, rightEdge As Long, downEdge As Long
With LeftTopCornerCell(1)
Set ws = .Parent
rightEdge = ws.Cells(.Row, ws.Columns.Count).End(xlToLeft).Column
downEdge = ws.Cells(ws.Rows.Count, .Column).End(xlUp).Row
End With
Set getTableRange = ws.Range(LeftTopCornerCell(1), ws.Cells(downEdge, rightEdge))
End Function
Your best solution might to set 2 ranges, each taking values from tables in Sheet1 and Sheet2. Let's call them rgSrcTable and rgDestTable. Then you need to loop using For Each through each range and compare top and left headers, and when you find a match, copy the value of the cell in rgSrcTable to the cell in rgDestTable.
Edit: Code sample. Feel free to adapt ranges to your needs. Since this routine used Range.Value property, you can filter any data (string, numbers, etc.)
Option Explicit
Sub CopyDataWithFilter()
Dim iRowHeader As Integer, iColHeader As Integer
Dim rngSrc As Range, rngDest As Range, celSrc As Range, celDest As Range
iRowHeader = 2
iColHeader = 1
With ThisWorkbook
' Set source and destination ranges. Modify ranges according to your needs
Set rngSrc = .Worksheets("shtSrc").Range("$B$3:$E$5")
Set rngDest = .Worksheets("shtDest").Range("$B$3:$E$5")
' Loop through source range and dest range
For Each celDest In rngDest
For Each celSrc In rngSrc
' Compare top headers and left headers respectively. If matching, copy the value in destination table.
If .Worksheets("shtSrc").Cells(celSrc.Row, iColHeader).Value = .Worksheets("shtDest").Cells(celDest.Row, iColHeader).Value And _
.Worksheets("shtSrc").Cells(iRowHeader, celSrc.Column).Value = .Worksheets("shtDest").Cells(iRowHeader, celDest.Column).Value Then
celDest.Value = celSrc.Value
End If
Next celSrc
Next celDest
End With
End Sub
Result:

Finding a range from different columns and inserting a value

My current challenge is that I have a list of data that has been pulled in from another workbook in Columns A-K which all finish on the same row. I want to label the data with all the same value in Column L
The code I have is trying to find the last cell in Column A and the last cell in Column L.
I am then trying to use the range between these two rows to insert a value across the range in Column L:
Sub FillTestType(TestType As String)
Dim rng As Range
Dim wsh As Worksheet
Dim Row As Long
Dim EndCell As Long
Dim TopCell As Long
Set wsh = Worksheets("Sheet1")
wsh.Activate
EndCell = Cells(Rows.count, "A").End(x1Up).Row
TopCell = Cells(Rows.count, "L").End(x1Up).Row
rng = Range(Cells(TopCell, 12), Cells(EndCell, 12)).Value
rng = TestType
End Sub
I keep getting a runtime error - Any help would be greatly appreciated! Or if I am being stupid and there is a better way to tackle the problem please do let me know.
Something like this should work. Mainly collating the comments:
xlUp rather than x1Up (and Option Explicit to catch that yourself in future!)
+1 to TopCell so that you don't overwrite your last value in that row
Fully qualified your Range and Cell references using With and .
Cleaned up variables which aren't necessary -- e.g. for multiple future use or readability
Option Explicit
Sub FillTestType(TestType As String)
Dim EndCell As Long
Dim TopCell As Long
With Worksheets("Sheet1")
EndCell = .Cells(.Rows.Count, "A").End(xlUp).Row
TopCell = .Cells(.Rows.Count, "L").End(xlUp).Row + 1
.Range(.Cells(TopCell, 12), .Cells(EndCell, 12)).Value = TestType
End With
End Sub

Create VBA for multiple sheet and copy paste in new column with formula

I have an excel with multiple sheets and would like to copy or better say want to extend the last column every month.
Eg:-
I have a sheet with sheet named sheet1,sheet2,sheet3,sheet4,sheet5...every sheet at the end of the month has formulas.Once a month is over I would like to add a new column with new month and copying the existing formula to the new column.Let say I have last month Jan and I need VBA to add new column with month as Feb and copy all the formula to the new column.
Sometimes I also need to copy multiple column (eg:-Column C-J) and replicate the next 8 column with new month and formula.
Tried with recording macro but the issue is it doesn't create a new column for every month it just copy paste it in same column rather than creating a new one for every month
It is difficult to understand the problem without seeing the formulas.
It sounds like you could start by using the AutoFill. You could do this manually by selecting the range you want to copy and dragging the cross in the bottom right corner. This will update the month automatically.
You can achieve this with VBA, such as:
Public Sub copyRange()
Dim rngSource As Range
Dim rngDestination As Range
rngSource = ActiveSheet.Range("A1:A20")
rngDestination = ActiveSheet.Range("B1:B20")
rngSource.AutoFill Destination:=rngDestination
End Sub
Either way, I can't tell how to reset the formulae for the new months without seeing the cell code.
UPDATE: To AutoFill multiple columns on multiple tabs
Public Sub copySpecifiedColumns()
copyRanges InputBox("How many columns do you wish to copy?", "Copy Columns", "1")
End Sub
Private Sub copyRanges(copyCols As Byte)
Dim ws As Worksheet, lastCol As Integer, lastRow As Integer
Dim rngSource As Range, rngDestination As Range
Dim sheetList As Variant
sheetList = Array("Sheet1", "Sheet2", "Sheet3", "Sheet4", "Sheet5")
For Each ws In ThisWorkbook.Sheets
If (UBound(Filter(sheetList, ws.Name)) > -1) Then
lastCol = ws.Cells(1, ws.Columns.Count).End(xlToLeft).Column
lastRow = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row
Set rngSource = ws.Range(ws.Cells(1, lastCol - copyCols + 1), _
ws.Cells(lastRow, lastCol))
Set rngDestination = ws.Range(ws.Cells(1, lastCol - copyCols + 1), _
ws.Cells(lastRow, lastCol + copyCols))
rngSource.AutoFill rngDestination
End If
Next ws
End Sub
I Agree it's a bit difficult to understand what you are trying to achieve here. From what I understand if you want to make a copy of last column in the next column in each sheet and change the 1st cell of that column to the month at the time. This code can help.
Sub copy_col()
Dim lColumn As Long
For Each Sheet In Worksheets
lColumn = Sheet.Cells(1, Columns.Count).End(xlToLeft).Column
Sheet.Columns(lColumn).Copy Sheet.Columns(lColumn + 1)
Sheet.Cells(1, lColumn + 1).Value = Month(Now())
Next Sheet
End Sub
If this is not what you want then please explain your problem more briefly.
Thanks
Extend List and Update Formula
Usage
ExtendList 5, "Sheet1", "Sheet3"
Where
1. 5, is the Column to Duplicate to the next empty Column
2. "Sheet1" is the sheet referenced in the original formula
3. "Sheet3" is the replace sheet name
Original Formula
=Sheet1!$A10
New Formula
=Sheet3!$A10
Sub ExtendList(SourceColumn As Long, OriginalSheetName As String, NewSheetName As String)
On Error Resume Next
Dim newColumnNumber As Integer
Worksheets(NewSheetName).Name = NewSheetName
If Err.Number <> 0 Then
Err.Clear
Exit Sub
End If
On Error GoTo 0
newColumnNumber = Range(Cells(1, Columns.Count), Cells(Rows.Count, Columns.Count)).End(xlToLeft).Offset(, 1).Column
Columns(SourceColumn).Copy Columns(newColumnNumber)
Columns(newColumnNumber).Replace What:=OriginalSheetName, Replacement:=NewSheetName, lookat:=xlPart, _
SearchOrder:=xlByColumns, MatchCase:=True, SearchFormat:=False, _
ReplaceFormat:=False
End Sub
This will only work if the column reference is absolute:
Correct
$A1 or $A$2
Incorrect
A1 or A$1

Find Column Header By Name And Select All Data Below Column Header (Excel-VBA)

I'm attempting to create a macro to do the following:
Search a spreadsheet column header by name.
Select all data from the selected column, except column header.
Take Number Stored As Text & Convert to Number.
Converting to Number to use for VLookup.
For Example:
Visual Spreadsheet Example:
I've discovered the following code online:
With ActiveSheet.UsedRange
Set c = .Find("Employee ID", LookIn:=xlValues)
If Not c Is Nothing Then
ActiveSheet.Range(c.Address).Offset(1, 0).Select
End If
End With
However, I'm still experiencing some issues.
I just stumbled upon this, for me the answer was pretty straightforward, in any case If you're dealing with a ListObject then this is the way to go:
YOURLISTOBJECT.HeaderRowRange.Cells.Find("A_VALUE").Column
It is good to avoid looping through all cells. If the data set grows the macro can become too slow. Using special cells and paste special operation of multiplying by 1 is an efficient way of accomplishing the task.
This works...
Dim SelRange As Range
Dim ColNum As Integer
Dim CWS As Worksheet, TmpWS As Worksheet
'Find the column number where the column header is
Set CWS = ActiveSheet
ColNum = Application.WorksheetFunction.Match("Employee ID", CWS.Rows(1), 0)
'Set the column range to work with
Set SelRange = CWS.Columns(ColNum)
'Add a worksheet to put '1' onto the clipboard, ensures no issues on activesheet
Application.ScreenUpdating = False
Application.DisplayAlerts = False
Set TmpWS = ThisWorkbook.Worksheets.Add
With TmpWS
.Cells(1, 1) = 1
.Cells(1, 1).Copy
End With
'Select none blank cells using special cells...much faster than looping through all cells
Set SelRange = SelRange.SpecialCells(xlCellTypeConstants, 23)
SelRange.PasteSpecial Paste:=xlPasteAll, Operation:=xlMultiply
TmpWS.Delete
CWS.Select
Application.DisplayAlerts = True
Application.ScreenUpdating = True
Try this out. Simply add all the column header names you want to find into the collection. I'm assuming you don't have more than 200 columns, if you do simply update the for i = 1 to 200 section to a larger number.
Public Sub FindAndConvert()
Dim i As Integer
Dim lastRow As Long
Dim myRng As Range
Dim mycell As Range
Dim MyColl As Collection
Dim myIterator As Variant
Set MyColl = New Collection
MyColl.Add "Some Value"
MyColl.Add "Another Value"
lastRow = ActiveSheet.Cells.Find("*", SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row
For i = 1 To 200
For Each myIterator In MyColl
If Cells(1, i) = myIterator Then
Set myRng = Range(Cells(2, i), Cells(lastRow, i))
For Each mycell In myRng
mycell.Value = Val(mycell.Value)
Next
End If
Next
Next
End Sub
Ok, here's a brief way of achieving your goal. First, locate the column that holds the Employee IDs. Then simply set the entire Column to be formatted as Number instead of Text?
With Worksheets(1) ' Change this sheet to the one you are using if not the first sheet
Set c = .Find("Employee ID", LookIn:=xlValues)
If Not c Is Nothing Then
' The column we want is c's Column.
Columns(c.Column).NumberFormat = 0
End If
End With
Add a dim for the range that you want:
Dim MyRng, RngStart, RngEnd as Range
Then change:
ActiveSheet.Range(c.Address).Offset(1, 0).Select
to the below so that all data in that column is found.
set RngStart = ActiveSheet.Cells(1, c.column)
set RngEnd = ActiveSheet.Cells(rows.count, c.column).end(xlup)
set MyRng = ActiveSheet.Range(RngStart & ":" & RngEnd)
Now you can play about with the data. If you want to paste this somewhere which is formatted as number:
MyRng.copy
Sheets("Wherever").Range("Wherever").pastespecial xlvalues
If you want to change the format of the cells you have now found (How to format column to number format in Excel sheet?) that is whole number format, if you want decimal points then use "number" instead of "0":
MyRng.NumberFormat = "0"
or the new destination:
Sheets("Wherever").Range("Wherever").NumberFormat = "0"
General formatting which matches exactly the convert to number function:
MyRng.NumberFormat = "General"
MyRng.Value = MyRng.Value

Selecting range and pasting into every nth column

Working on some code to copy an active range of cells in column E and then paste the range (starting from the same row) into every nth column. The copy selection part works fine but I can't get the right syntax for the pasting part. I tried setting the cells equal to the selection as I wasn't sure on appropriate syntax to paste values of a selection. Any help/guidance is appreciated! Here is my code:
Sub Sco__copy()
Dim cpval As Range
Dim lastRow As Long
With Worksheets("Sheet1")
lastRow = .Cells(Rows.Count, "E").End(xlUp).Row
.Range("E163:E" & lastRow).Select
Set cpval = Selection
End With
For colx = 12 To 1000 Step 7
Cells(lastRow, colx).Value = cpval
Next
End Sub
Try this:
Sub Sco__copy()
Dim cpval As Range
Dim lastRow As Long
With Worksheets("Sheet1")
lastRow = .Cells(Rows.Count, "E").End(xlUp).Row
Set cpval = .Range("E163:E" & lastRow)
For colx = 12 To 1000 Step 7
.Range(.Cells(163, colx), .Cells(lastRow, colx)).Value = cpval.Value
Next
End With
End Sub
The idea is that when assigning values, the target range needs to be the same size as the original.
So by extending the with statement and creating a range the same size it should place the cells in every 7th column.