How to copy specific ranges into a new worksheet in VBA? - excel

I'm trying to create a macro that will compile specific columns from all worksheets in a workbook into a single new worksheet.
What I have so far creates the new sheet, and returns the correct headers for each column, but copies across all columns from the existing sheets rather than just the columns I have specified.
As can be seen with the column headings, I would like to only copy the values in columns A:I, K:M, R and W:Y from sheets 2 onwards, into columns B:O in the "MASTER" worksheet.
Does anyone have any suggestions as to how I can get this working?
Sub Combine2()
Dim J As Integer, wsNew As Worksheet
Dim rngCopy As Range, rngPaste As Range
Dim Location As String
On Error Resume Next
Set wsNew = Sheets("MASTER")
On Error GoTo 0
'if sheet does not already exist, create it
If wsNew Is Nothing Then
Set wsNew = Worksheets.Add(Before:=Sheets(1)) ' add a sheet in first place
wsNew.Name = "MASTER"
End If
'copy headings and paste to new sheet starting in B1
With Sheets(2)
.Range("A1:I1").Copy wsNew.Range("B1")
.Range("R1").Copy wsNew.Range("K1")
.Range("K1:M1").Copy wsNew.Range("L1")
.Range("W1:Y1").Copy wsNew.Range("O1")
End With
' work through sheets
For J = 2 To Sheets.Count ' from sheet 2 to last sheet
'save sheet name/location to string
Location = Sheets(J).Name
'set range to be copied
With Sheets(J).Range("A1").CurrentRegion
Set rngCopy = .Offset(1, 0).Resize(.Rows.Count - 1)
End With
'set range to paste to, beginning with column B
Set rngPaste = wsNew.Cells(Rows.Count, 2).End(xlUp).Offset(1, 0)
'copy range and paste to column *B* of combined sheet
rngCopy.Copy rngPaste
'enter the location name in column A for all copied entries
Range(rngPaste, rngPaste.End(xlDown)).Offset(0, -1) = Location
Next J
With Sheets(1)
Range("A1").Value = "Extract Date"
Range("A1").Font.Bold = True
Columns("A:T").AutoFit
End With
' wsNew.Visible = xlSheetHidden
End Sub

Copy/paste each range in turn in the same way as you have for the headings. (untested)
Dim ar(4), k as Integer
ar(1) = array("A1:I1","B")
ar(2) = array("R1","K")
ar(3) = array("K1:M1","L")
ar(4) = array("W1:Y1","O")
'copy headings and paste to new sheet
With Sheets(2)
For k = 1 to Ubound(ar)
.Range(ar(k)(0)).Copy wsNew.Range(ar(k)(1) & "1")
Next
End With
' work through sheets
Dim lr As Long
For J = 2 To Sheets.Count ' from sheet 2 to last sheet
'save sheet name/location to string
Location = Sheets(J).Name
'set range to be copied
With Sheets(J)
lr = .Cells(Rows.Count, 1).End(xlUp).Row
For k = 1 to Ubound(ar)
Set rngCopy = .Range(ar(k)(0)).Offset(1).Resize(lr-1)
'set range to paste to, beginning with column B
Set rngPaste = wsNew.Cells(Rows.Count, ar(k)(1)).End(xlUp).Offset(1, 0)
'copy range and paste to combined sheet
rngCopy.Copy rngPaste
If k = 1 Then
'enter the location name in column A for all copied entries
Range(rngPaste, rngPaste.End(xlDown)).Offset(0, -1) = Location
End If
Next
End With
Next J
Note this block is missing a dot on the ranges to use the With
With Sheets(1)
Range("A1").Value = "Extract Date"
Range("A1").Font.Bold = True
Columns("A:T").AutoFit
End With

Related

VBA code that uses checkbox to copy and paste entire row with data to a new sheet

I have a workbook that contains several sheets with different types of inventory and one summary sheet.
I am trying to use checkboxes, that if checked as "True", will copy that row of data and paste into the summary sheet starting on a specific row. Each inventory sheet has several rows of differing data and I'd like users to be able to check multiple boxes they need on each sheet and this data to be copied to the summary sheet.
I found this code below that is working for the most part except it skips over some lines of data that are marked as "true". It also adds an unnecessary extra row between the lines once it copies the data over to the new sheet. What can I change so that all of the data marked "true" can be copied over and eliminate the extra rows?
Code I found is from this video: https://www.youtube.com/watch?v=TJoRUwrEe0g
Sub CopyRowBasedOnCellValue()
Dim xRg As Range
Dim xCell As Range
Dim A As Long
Dim B As Long
Dim C As Long
A = Worksheets("Exterior Items").UsedRange.Rows.Count
B = Worksheets("Customer Sheet").UsedRange.Rows.Count
If B = 1 Then
If Application.WorksheetFunction.CountA(Worksheets("Customer Sheet").UsedRange) = 0 Then B = 0
End If
Set xRg = Worksheets("Exterior Items").Range("B1:B" & A)
On Error Resume Next
Application.ScreenUpdating = False
For B = 1 To xRg.Count
If CStr(xRg(B).Value) = "True" Then
xRg(B).EntireRow.Copy Destination:=Worksheets("Customer Sheet").Range("A" & B + 9)
B = B + 1
End If
Next
Application.ScreenUpdating = True
End Sub
Try this:
Sub Copy_table_where_B_is_TRUE_row_by_row()
'declarations
Dim shtSource As Worksheet
Dim shtDestination As Worksheet
'reference source and destination sheets
Set shtSource = Worksheets("Exterior Items")
Set shtDestination = Worksheets("Customer Sheet")
'find limits of tables present on source and destination sheets
LastRowSource = shtSource.Cells(shtSource.Rows.Count, "A").End(xlUp).Row
LastRowDestination = shtDestination.Cells(shtDestination.Rows.Count, "A").End(xlUp).Row
'set output row index
OutputRow = LastRowDestination + 1
'using the source table..
For Each rw In shtSource.Range("1:" & LastRowSource).Rows
'if 2nd cell in row is TRUE
If rw.Cells(2).Value = "True" Then
'copy to destination sheet
rw.Copy shtDestination.Cells(OutputRow, 1)
'increment output row index
OutputRow = OutputRow + 1
End If
Next
End Sub
An entirely different method, that doesn't require any loops or counters would be to use a filter:
Sub Copy_filtered_table_where_B_is_TRUE()
'declarations
Dim shtSource As Worksheet
Dim shtDestination As Worksheet
'reference source and destination sheets
Set shtSource = Worksheets("Exterior Items")
Set shtDestination = Worksheets("Customer Sheet")
'find limits of tables present on source and destination sheets
'(these can be manually set if source table is fixed and destination location is fixed)
LastRowSource = shtSource.Cells(shtSource.Rows.Count, "A").End(xlUp).Row
LastRowDestination = shtDestination.Cells(shtDestination.Rows.Count, "A").End(xlUp).Row
'using the source table..
With shtSource.Range("1:" & LastRowSource)
'apply a filter
.AutoFilter
'set filter to column 2 = True
.AutoFilter Field:=2, Criteria1:="True"
'copy cells visible after application of filter, to next available row on destination sheet
.SpecialCells(xlCellTypeVisible).Copy shtDestination.Cells(LastRowDestination + 1, 1)
'remove filter
shtSource.AutoFilterMode = False
End With
End Sub

Use VBA to copy entire row from one excel worksheet to another if match is not found in Column A

I have been running into some issues trying to use VBA to compare 2 tables in different worksheets, and then copy any rows in the "Master" sheet that are not found in the "New" sheet. Both tables are formatted as tables. The match is based on an "ID" column in Column A of both tables. If an ID is in the "Master" sheet, but not in the "New" sheet, than that entire row should be copy and pasted to the end of the table in the "New" sheet.
I updated some code found in another forum, which is almost working. However, it only seems to paste over the ID data into Column A, and not the entire corresponding row of data which is needed.
Sub compare()
Dim i As Long
Dim lrs As Long
Dim lrd As Long
With Worksheets("Master")
lrs = .Cells(.Rows.Count, 1).End(xlUp).Row
For i = 2 To lrs 'assumes header in row 1
If Application.IfError(Application.Match(.Cells(i, 1), Worksheets("New").Columns(1), 0), 0) = 0 Then
lrd = Worksheets("New").Cells(Worksheets("test").Rows.Count, 1).End(xlUp).Row
Worksheets("New").Cells(lrd + 1, 1).Value = .Cells(i, 1).Value
End If
Next i
End With
End Sub
I think the issue has to do with the "Cells" reference, instead of a range, but I do not know how to make that line dynamic.
Slightly different approach, but you need to use something like Resize() to capture the whole row, and not just the cell in Col A.
Sub compare()
Const NUM_COLS As Long = 10 'for example
Dim wb As Workbook, wsSrc As Worksheet, wsDest As Worksheet
Dim c As Range, cDest As Range
Set wb = ThisWorkbook 'or ActiveWorkbook for example
Set wsSrc = wb.Worksheets("Master")
Set wsDest = wb.Worksheets("New")
Set cDest = wsDest.Cells(Rows.Count, 1).End(xlUp).Offset(1) 'next empty row
For Each c In wsSrc.Range("A2:A" & wsSrc.Cells(Rows.Count, 1).End(xlUp).Row).Cells
If IsError(Application.Match(c.Value, wsDest.Columns(1), 0)) Then
cDest.Resize(1, NUM_COLS).Value = c.Resize(1, NUM_COLS).Value
Set cDest = cDest.Offset(1) 'next row
End If
Next c
End Sub

VBA Copy and Paste function where two criteria are met

I am brand new to VBA coding and am confused on how I would be able to copy and paste values from one sheet to another if two criteria points are met. In the sheet below I want to copy "12, 9, and 15" and paste it into the "Expected, P10 and P90" cells on sheet2 if the names on sheet one "Orange, Green" match those on sheet 1.
I've been attempting this on my own for quite some time now with now luck.
Attached is the code I started
Sub Copy_Certain_Data()
a = Worksheets("Schedule Results").Cells(Rows.Count, 1).End(xlUp).Row
For i = 3 To a
If Worksheets("Schedule Results").Cells(i, 3).Value = "NE2P1" Then
Worksheets("schedule results").Rows(i).Copy
Worksheets("Campaign 1 Data").Activate
Range("F2").Select
ActiveSheet.Paste
Worksheets("Schedule Results").Activate
End If
Next
Application.CutCopyMode = False
End Sub
Below is a basic macro to loop through two worksheets and find the row that has matching values in columns A and B. Then writing the values from the row in sheet 1, columns C:E to the row in sheet 2, columns D:F.
Dim ws1 As Worksheet, ws2 As Worksheet
Dim xCel As Range, yCel As Range
Set ws1 = ThisWorkbook.Sheets("Sheet1") 'change sheet names as needed
Set ws2 = ThisWorkbook.Sheets("Sheet2")
For Each xCel In ws1.Range("A2", ws1.Range("A" & ws1.Rows.Count).End(xlUp)) 'loop sheet1 column A
If xCel.Value = "Orange" And xCel.Offset(, 1).Value = "Green" Then 'when both values are found in row goto sheet2 loop
For Each yCel In ws2.Range("A2", ws2.Range("A" & ws2.Rows.Count).End(xlUp)) 'Loop sheet2 Column A
If yCel.Value = "Orange" And yCel.Offset(, 1).Value = "Green" Then 'when found write values from sheet1 to sheet2
yCel.Offset(, 3).Resize(, 3).Value = xCel.Offset(, 2).Resize(, 3).Value
End If
Next yCel
End If
Next xCel
This should give you a start to get you what you are trying to accomplished based on the code you have tried. Its always best practice to set your variables and also qualify worksheets.
Using .copy and .paste can cause issues because if the cells are not the same size you will get an error stating such and that is why I always set the destination cell value = the source cell value.
Option Explict
Sub Copy_Certain_Data()
Dim wb As Workbook
Dim wsSource As Worksheet
Dim wsDest As Worksheet
Set wb = ThisWorkbook
Set wsSource = wb.Sheets("Schedule Results")
Set wsDest = wb.Sheets("Campaign 1 Data")
Dim LastRow As Long, i As Long
LastRow = wsSource.Cells(wsSource.Rows.Count, "A").End(xlUp).Row
For i = 3 To LastRow
If wsSource.Cells(i, 3).Value = "NE2P1" Then
wsDest.Cells(i, 6) = wsSource.Cells(i, 3)
End If
Next i
End Sub

Cut rows to new sheet based on values in column

I have this list of products, and i want to:
Create new sheets based on the values on column C, if there's already a sheet with the same name as the cell value don't create a new sheet. (like "Abstract" in my example that already been created for row 2 and doesn't need to created again for row 3)
Cut the entire row to the matching sheet.
Make sure the first row is copied to all sheets.
This is a before picture
After Pic #1: new sheets created, nothing left on first sheet except the 1st row
After Pic #2: the sheet contains 2 products because there were 2 "Abstract" in column C
After Pic #3: the sheet contain 1 product because there was 1 "Plain" in column C
After Pic #4: the sheet contain 1 product because there was 1 "Shiny" in column C
This will get the job done.
I Named the first sheet to "Worksheet".
The code is dynamic, so you need to input 2 values by yourself:
Which range/names that should create the new worksheets:
Set myrange = ThisWorkbook.Sheets("Worksheet").Range("C2:C5") 'Set range that should create the new worksheet list
and how many columns you want to copy to the new sheets (it makes it more dynamic than take the whole row)
lastcol = Cells(1, "C").Column 'Set how many column that should be copied to new worksheet
VBA Code:
Sub AddNewSheetFromRange2()
Dim c As Range
Dim ws As Worksheet
Dim myrange As Range
Dim lastcol As Integer
Dim lrow As Integer
Dim lrow_newsheet As Integer
Dim i As Integer
Set myrange = ThisWorkbook.Sheets("Worksheet").Range("C2:C5") 'Set range that should create the new worksheet list
lastcol = Cells(1, "C").Column 'Set how many column that should be copied to new worksheet
lrow = Cells(Rows.Count, 3).End(xlUp).Row 'find last row for range that should create the new worksheet list
i = 1 'Set first index loop to 1
For Each c In myrange.Cells
i = i + 1 'Create index for each loop, used to know which row that should be copied
'Debug.Print c 'Print which Sheet Name that will be examine
Set ws = Nothing
On Error Resume Next
Set ws = Worksheets(c.Value)
On Error GoTo 0
If ws Is Nothing Then
With ThisWorkbook
Set ws = .Sheets.Add(After:=.Sheets(.Sheets.Count)) 'Add new sheet after (not before)
ws.Name = c.Value 'Rename the new sheet
End With
Dim WorksheetSheet As Worksheet 'Declare variable for Main worksheet
Set WorksheetSheet = ActiveWorkbook.Worksheets("Worksheet") 'Name the Main sheet
Dim NewSheet As Worksheet 'Declare variable for new worksheet
Set NewSheet = ActiveWorkbook.Worksheets(ws.Name) 'Make all new worksheets dynamic by taking name from range
'Copy Headers from Main sheet to New Worksheet
Worksheets("Worksheet").Activate
ThisWorkbook.Worksheets("Worksheet").Range(Cells(1, 1), Cells(1, 3)).Copy
Worksheets(ws.Name).Activate
ThisWorkbook.Worksheets(ws.Name).Range(Cells(1, 1), Cells(1, 3)).PasteSpecial
'Copy row from Main sheet to New Worksheet
Worksheets("Worksheet").Activate
ThisWorkbook.Worksheets("Worksheet").Range(Cells(i, 1), Cells(i, lastcol)).Copy
Worksheets(ws.Name).Activate
lrow_newsheet = Cells(Rows.Count, 3).End(xlUp).Row + 1
ThisWorkbook.Worksheets(ws.Name).Range(Cells(lrow_newsheet, 1), Cells(lrow_newsheet, lastcol)).PasteSpecial
'Clear row in Main sheet
Worksheets("Worksheet").Activate
ThisWorkbook.Worksheets("Worksheet").Range(Cells(i, 1), Cells(i, lastcol)).Clear
Else
'If worksheet already exists, then
'Copy row from Main sheet to existing worksheet with exactly the same name
Worksheets("Worksheet").Activate
ThisWorkbook.Worksheets("Worksheet").Range(Cells(i, 1), Cells(i, lastcol)).Copy
Worksheets(ws.Name).Activate
lrow_newsheet = Cells(Rows.Count, 3).End(xlUp).Row + 1
ThisWorkbook.Worksheets(ws.Name).Range(Cells(lrow_newsheet, 1), Cells(lrow_newsheet, lastcol)).PasteSpecial
'Clear row in Main sheet
Worksheets("Worksheet").Activate
ThisWorkbook.Worksheets("Worksheet").Range(Cells(i, 1), Cells(i, lastcol)).Clear
End If
Next c
End Sub
Visualizing the code in excel you will have to start with this:
and the final output will be this (the four rows into individual worksheets, if the name already exists, it will add to the already existing worksheet)

Loop through worksheets, paste data in another worksheet in columns with matching name

I want to merge tables from multiple Excel sheets with uncommon and common column names.
I can't get the loop to go to sheets in my workbook and paste in the combine worksheet.
For example I have the following tables:
Sheet1:
name surname color
Eva x
steven y black
Mark z white
Sheet2:
Surname color name code
L Green Pim 030
O yellow Xander 34
S Rihanna 567
My third sheet (the combine sheet) has all the possible column names of all sheets so it looks like:
name surname color code
The macro should read Sheet1 and Sheet2 then paste data in the combine sheet under the correct column name.
The combine sheet should looks like this, with the elements of Sheet2 under the elements of Sheet1:
name surname color code
Eva x
steven y black
Mark z white
Pim L Green 030
Xander O yellow 34
Rihanna S 567
I couldn't get the loop to read then paste data in the right column.
Sub CopyDataBlocks_test2()
'VARIABLE NAME 'DEFINITION
Dim SourceSheet As Worksheet 'The data to be copied is here
Dim CombineSheet As Worksheet 'The data will be copied here
Dim ColHeaders As Range 'Column headers on Combine sheet
Dim MyDataHeaders As Range 'Column headers on Source sheet
Dim DataBlock As Range 'A single column of data
Dim c As Range 'a single cell
Dim Rng As Range
'The data will be copied here (="Place holder" for the first data cell)
Dim i As Integer
'Dim WS_Count As Integer 'for all sheets in active workbook
'Dim j As Integer 'Worksheets count
'Change the names to match your sheetnames:
Set SourceSheet = Sheets(2)
Set CombineSheet = Sheets("Combine")
With CombineSheet
Set ColHeaders = .Range(.Cells(1, 1), .Cells(1, .Columns.Count).End (xlToLeft))
Set Rng = .Cells(.Rows.Count, 1).End(xlUp).Offset(1)
End With
With SourceSheet
Set MyDataHeaders = .Range(.Cells(1, 1), .Cells(1, .Columns.Count).End(xlToLeft))
For Each c In MyDataHeaders
If Application.WorksheetFunction.CountIf(ColHeaders, c.Value) = 0 Then
MsgBox "Can't find a matching header name for " & c.Value & _
vbNewLine & "Make sure the column names are the same and try again."
Exit Sub
End If
Next c
'A2:A & the last cell with something on it on column A
Set DataBlock = .Range(.Cells(2, 1), .Cells(.Rows.Count, 1).End(xlUp))
Set Rng = Rng.Resize(DataBlock.Rows.Count, 1)
For Each c In MyDataHeaders
i = Application.WorksheetFunction.Match(c.Value, ColHeaders, 0)
'Writes the values
Rng.Offset(, i - 1).Value = Intersect(DataBlock.EntireRow, c.EntireColumn).Value
Next c
End With
End Sub
you just wrap your With SourceSheet - End With block code into a For each sourceSheet in Worksheets - Next loop checking not to process "Combine" sheet itself
it'd be cleaner to move that into a helper Sub like follows:
Option Explicit
Sub CopyDataBlocks_test2()
'VARIABLE NAME 'DEFINITION
Dim sourceSheet As Worksheet 'The data to be copied is here
Dim ColHeaders As Range 'Column headers on Combine sheet
With Worksheets("Combine") '<--| data will be copied here
Set ColHeaders = .Range(.Cells(1, 1), .Cells(1, .Columns.Count).End(xlToLeft))
For Each sourceSheet In Worksheets '<--| loop through all worksheets
If sourceSheet.Name <> .Name Then ProcessSheet sourceSheet, ColHeaders, .Cells(.Rows.Count, 1).End(xlUp).Offset(1) '<--| process data if not "Combine" sheet
Next
End With
End Sub
Sub ProcessSheet(sht As Worksheet, ColHeaders As Range, rng As Range)
Dim MyDataHeaders As Range 'Column headers on Source sheet
Dim c As Range 'a single cell
Dim i As Integer
Dim DataBlock As Range 'A single column of data
With sht
Set MyDataHeaders = .Range(.Cells(1, 1), .Cells(1, .Columns.Count).End(xlToLeft))
For Each c In MyDataHeaders
If Application.WorksheetFunction.CountIf(ColHeaders, c.Value) = 0 Then
MsgBox "In worksheet " & .Name & " can't find a matching header name for " & c.Value & vbNewLine & "Make sure the column names are the same and try again."
Exit Sub
End If
Next c
Set DataBlock = .Range(.Cells(2, 1), .Cells(.Rows.Count, 1).End(xlUp)) 'A2:A & the last cell with something on it on column A
For Each c In MyDataHeaders
i = Application.WorksheetFunction.Match(c.Value, ColHeaders, 0)
rng.Offset(, i - 1).Resize(DataBlock.Rows.Count, 1).Value = DataBlock.Columns(c.Column).Value 'Writes the values
Next c
End With
End Sub

Resources