Copy specific Rows from one workbook to another - vba

I'm having trouble copying specific Rows with vba.
Here my Code:
Dim color1 As Integer
Dim color2 As Integer
Dim lines As Integer
Workbooks.Open Filename:="D:\01 January.xlsm", _
UpdateLinks:=0
lines = WorksheetFunction.CountA(Range("U:U")) - 1
Dim i As Integer
For i = 6 To lines + 6
color1 = Cells(i, 21).Value
color2 = Cells(i, 22).Value
If IsNumeric(Cells(i, 21)) Then
Select Case color1 & color2
Case Evaluate("=White") & Evaluate("=Blue")
Rows(i & ":" & i).Select
Case Evaluate("=Yellow") & Evaluate("=Yellow")
Rows(i & ":" & i).Select
Case Evaluate("=Yellow") & Evaluate("=Green")
Rows(i & ":" & i).Select
End Select
End If
Next i
Selection.Copy
Windows("Test.xlsm").Activate
Rows("11:11").Select
Selection.PasteSpecial Paste:=xlPasteFormats, Operation:=xlNone, _
SkipBlanks:=False, Transpose:=False
Selection.PasteSpecial Paste:=xlPasteFormulas, Operation:=xlNone, _
SkipBlanks:=False, Transpose:=False
End Sub
So as you might see, I am trying to select Rows, that meet the criteria in the January.xlsm and paste them afterwards into the test.xlsm
At the moment it only pastes the last selected row and not all of them.
I'm pretty new to vba, so I would really need your help here. What I got in my mind, is to put all the needed rows into an array and then copy it into the other workbook. But no idea if thats good or just rubish and if that would work, I can't find a solution...
Thanks for all your help!

The reason it only pastes the last row is because you're looping through selecting the individual rows but not doing anything with them. See amended code.
I've removed the redundant selections in the case statement and provided a range/union combo to create your custom range to ensure you're only pasting to the worksheet once.
Dim color1 As Integer
Dim color2 As Integer
Dim lines As Integer
Workbooks.Open Filename:="D:\01 January.xlsm", _
UpdateLinks:=0
lines = WorksheetFunction.CountA(Range("U:U")) - 1
Dim i As Integer
Dim rngUnion As Range
Dim booCopy As Boolean
For i = 6 To lines + 6
booCopy = True
color1 = Cells(i, 21).Value
color2 = Cells(i, 22).Value
If IsNumeric(Cells(i, 21)) Then
Select Case color1 & color2
Case Evaluate("=White") & Evaluate("=Blue")
Case Evaluate("=Yellow") & Evaluate("=Yellow")
Case Evaluate("=Yellow") & Evaluate("=Green")
Case Else
booCopy = False
End Select
End If
If booCopy = True Then
If rngUnion Is Nothing Then
Set rngUnion = Rows(i & ":" & i)
Else
Set rngUnion = Union(rngUnion, Rows(i & ":" & i))
End If
End If
Next i
If Not rngUnion Is Nothing Then
rngUnion.Copy
Windows("Test.xlsm").Activate
With Rows("11:11")
.PasteSpecial Paste:=xlPasteFormats, Operation:=xlNone, _
SkipBlanks:=False, Transpose:=False
.PasteSpecial Paste:=xlPasteFormulas, Operation:=xlNone, _
SkipBlanks:=False, Transpose:=False
End With
Application.CutCopyMode = False
End If
End Sub

The reason this only pastes the last selected row is because you are not copying and pasting within the loop. If you move the Selection.Copy/Paste within the loop the code should work. A better way to do this would be to avoid copying and pasting entirely and directly set the values of the rows. See code below:
Dim i As Integer
For i = 6 To lines + 6
color1 = Cells(i, 21).Value
color2 = Cells(i, 22).Value
If IsNumeric(Cells(i, 21)) Then
Select Case color1 & color2
Case Evaluate("=White") & Evaluate("=Blue"):
Workbooks("Test").Sheets("Sheet1").Rows(i).Value = _
Workbooks("01 January").Sheets("Sheet1").Rows(i).Value
...
End Select
End If
Next i
You can just update the sheet or workbook names as necessary but this method is substantially faster than copying and pasting.

should you have a large number of rows to be copied and paste it's safer not to rely neither on Union() nor Address() methods and switch to a "helper" column where to first mark the row for copying and then copy and paste in one shot. This is also much faster then the two methods above
you can also take advantage of SpecialCells() method to filter "numeric" cells only:
Dim lines As Long
Dim cell As Range
Workbooks.Open Filename:="D:\01 January.xlsm", UpdateLinks:=0
lines = WorksheetFunction.CountA(Range("U:U")) - 1
With Range(Cells(6, "U"), Cells(lines + 6, "U")) '<--| reference your relevant range in column "U"
For Each cell In .SpecialCells(xlCellTypeConstants, xlNumbers) '<--| loop through "numeric" cells only
Select Case cell.Value & cell.Offset(, 1).Value
Case Evaluate("=White") & Evaluate("=Blue"), Evaluate("=Yellow") & Evaluate("=Yellow"), Evaluate("=Yellow") & Evaluate("=Green")
cell.Offset(, 2).Value = 1 '<--| mark row for copying&pasting
End Select
Next
With .Offset(, 2) '<-- consider column "W" cells corresponding to referenced cells
If WorksheetFunction.CountA(.Cells) > 0 Then '<--| if there's at least one row marked for copy&paste
.SpecialCells(xlCellTypeConstants, xlNumbers).EntireRow.Copy '<--| copy all marked rows
With Workbooks("Test.xlsm").ActiveSheet.Rows("11:11") '<--| reference target range
.PasteSpecial Paste:=xlPasteFormats, Operation:=xlNone, _
SkipBlanks:=False, Transpose:=False
.PasteSpecial Paste:=xlPasteFormulas, Operation:=xlNone, _
SkipBlanks:=False, Transpose:=False
End With
Application.CutCopyMode = False '<--| clear clipboard
End If
.ClearContents '<--| clear "helper" column
End With
End With

Related

Pasting row from one sheet to the other to first blank row

I have seen some examples but they have been using .Select and .Activate. I am trying to learn how to not use those anymore because everyone says you should try to stay away from them.
I want to take a row, then copy it to the first blank row on the other sheet. I was close but it just isn't working.
UsdRws = Range("A" & Rows.Count).End(xlUp).Row
With Sheets("Totals by Department")
.Range("A1:Z" & UsdRws).autofilter Field:=1, Criteria1:="1450"
.Range("A2:Z" & UsdRws).SpecialCells(xlCellTypeVisible).EntireRow.COPY
End With
Set NextRow = Range("A" & Sheets(2).UsedRange.Rows.Count + 1)
Sheets(2).NextRow.PasteSpecial Paste:=xlValues, Operation:=xlNone, SkipBlanks:=False, Transpose:=False
Application.CutCopyMode = False
Set NextRow = Nothing
The first part copies perfectly, I really just need help pasting it over on the other sheet. I will also take other recommendations for cleaning the code up. Like I said I am trying to learn to write better. The second part is messy because I have been adding and editing it but now I am lost.
Your "NextRow" object is a Range object, but you are calling it as if it were a method or property of Sheets(2).
Try removing the Sheets(2). and just start with Next Row.
Set NextRow = Sheets(2).Range("A" & Sheets(2).UsedRange.Rows.Count + 1)
NextRow.PasteSpecial Paste:=xlValues, Operation:=xlNone, SkipBlanks:=False, Transpose:=False
Application.CutCopyMode = False
' UsdRws is equal the last used row on whichever sheet is active at the moment that this code runs
UsdRws = Range("A" & Rows.Count).End(xlUp).Row
' this code properly references ranges on a specific worksheet, regardless of which worksheet is active
With Sheets("Totals by Department")
.Range("A1:Z" & UsdRws).autofilter Field:=1, Criteria1:="1450"
.Range("A2:Z" & UsdRws).SpecialCells(xlCellTypeVisible).EntireRow.COPY
End With
' NextRow is reference to a cell on whichever sheet is active at the moment that this code runs
' but the row referenced is same as the first emply cell on Sheets(2)
Set NextRow = Range("A" & Sheets(2).UsedRange.Rows.Count + 1)
' NextRow is already a range .... so it should be NextRow.PasteSpecial ......
Sheets(2).NextRow.PasteSpecial Paste:=xlValues, Operation:=xlNone, SkipBlanks:=False, Transpose:=False
Application.CutCopyMode = False
Set NextRow = Nothing
this may be what you want
With Sheets("Totals by Department")
UsdRws = .Range("A" & .Rows.Count).End(xlUp).Row
.Range("A1:Z" & UsdRws).autofilter Field:=1, Criteria1:="1450"
.Range("A2:Z" & UsdRws).SpecialCells(xlCellTypeVisible).EntireRow.COPY
End With
Set NextRow = Sheets(2).Range("A" & Sheets(2).UsedRange.Rows.Count + 1)
NextRow.PasteSpecial Paste:=xlValues, Operation:=xlNone, SkipBlanks:=False, Transpose:=False
Application.CutCopyMode = False
Set NextRow = Nothing

Paste copied values with .PasteSpecial

Please help me for this problem:
I use this vba from this link:
Sub test()
Dim rng1 As Range, rng2 As Range, rngName As Range, i As Integer, j As Integer
For i = 1 To Sheets("Sheet2").Range("B" & Rows.Count).End(xlUp).Row
Set rng1 = Sheets("Sheet2").Range("B" & i)
For j = 1 To Sheets("Sheet1").Range("C" & Rows.Count).End(xlUp).Row
Set rng2 = Sheets("Sheet1").Range("C" & j)
Set rngName = Sheets("Sheet1").Range("B" & j)
If rng1.Value = rng2.Value Then
rngName.Copy Destination:=Worksheets("Sheet2").Range("E" & i)
End If
Set rng2 = Nothing
Next j
Set rng1 = Nothing
Next i
End Sub
Can somebody show me how to combine rngName.Copy with
Selection.PasteSpecial Paste:=xlPasteFormulas, Operation:=xlNone, _
SkipBlanks:=False, Transpose:=False
I would like to earn, that the rngName.Copy copy only text, because I have special color, text format, comment etc. in the cells, where the vba paste the changed values and I would like to stay these.
Do you mean this?
rngName.Copy
Worksheets("Sheet2").Range("E" & i).PasteSpecial Paste:=xlPasteFormulas, _
Operation:=xlNone, SkipBlanks:=False, Transpose:=False
You may also want xlPasteValues instead of xlPasteFormulas. In this case, a simpler and better way is to take the value without using copy/paste:
Worksheets("Sheet2").Range("E" & i).Value = rngName.Value
All of the methods above conserve the formatting of the destination.

Dynamic Ranges using offset function

Sub FIXPAY()
Dim LastCol As Long, LastRow As Long, s2 As Worksheet
Set s2 = Sheets("Analysis")
s2.Select
Range("A2").Select
Range(Selection, Selection.End(xlToRight)).Copy
s2.Range("A" & Rows.Count).End(xlUp).Offset(2, 0).PasteSpecial xlPasteAll
'*Copy the table header from 1st table and paste it to the second empty row.*
Range("A3:C3").Select
Range(Selection, Selection.End(xlDown)).Copy
s2.Range("A" & Rows.Count).End(xlUp).Offset(1, 0).Select
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
Application.CutCopyMode = False
'*Copy from A3:C3 to the last filled row and paste it to the row right below
the heading.*
s2.Range("D" & Rows.Count).End(xlUp).Offset(1, 0).FormulaR1C1 = _
"=INDIRECT(""'""&RC1&""'!""&ADDRESS(ROW(R73C[-2]),COLUMN(R73C[-2])))"
'*Put the above formula in the D Column right below the heading*
The problem lies here.How can I replace the below line with dynamic range such that it will auto-fill the data in the D Column
s2.Range("D" & Rows.Count).End(xlUp).Offset(1, 0).FormulaR1C1 = _
"=INDIRECT(""'""&RC1&""'!""&ADDRESS(ROW(R73C[-2]),COLUMN(R73C[-2])))"
s2.Range("D15").AutoFill destination:=Range("D15:D" & Range("A15").End(xlDown).Row)
LastCol = Cells(14, Columns.Count).End(xlToLeft).Column
LastRow = Range("A" & Rows.Count).End(xlUp).Row
Range("D15", Cells(LastRow, LastCol)).FormulaR1C1 = Range("D15").FormulaR1C1
End Sub
Can someone please help me making the code fully dynamic? Please note that I have referenced as row 15 here because my heading of the second table is in row 14 and hence the formula from D15 is to be copied to the last filled row and last filled column.

VBA: Build a Table by (Copy/Paste) by Using Criteria to Select Rows, Then Specifiy Columns

I want to build a table on one Excel Sheet "Ship" by pulling data from another excel sheet "Efficiency." The row data on the "Efficiency" sheet is categorized by "Shipped", "Leave", "Import" and "Export".
Each category (shipped, leave, import, export) has several items and they're in no specific order. The table on the "Efficiency" sheet occupies columns A:H, and starts at row 2; the length can vary.
I want to be able to search the rows for "Shipped" and copy columns A, D:F and H of the matching rows and paste them beginning at cell B4 of the "Ship" sheet. Can anyone help me please?
Sub Ship()
ActiveSheet.Range("$A$1:$H$201").AutoFilter Field:=4, Criteria1:="Shipped"
' this is looking in a specific range, I want to make it more dynamic
Range("A4:A109").Select
'This is the range selected to copy, again I want to make this part more dynamic
Application.CutCopyMode = False
Selection.Copy
Range("A4:A109,D4:F109,H4:H109").Select
Range("G4").Activate
Application.CutCopyMode = False
Selection.Copy
Sheets("Ship").Select
Range("B4").Select
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
End Sub
This code has been tested based on your the information as given in your question:
Sub Ship()
Dim wsEff As Worksheet
Dim wsShip As Worksheet
Set wsEff = Worksheets("Efficiency")
Set wsShip = Worksheets("Shipped")
With wsEff
Dim lRow As Long
'make it dynamic by always finding last row with data
lRow = .Range("A" & .Rows.Count).End(xlUp).Row
'changed field to 2 based on your above comment that Shipped is in column B (the code you posted has 4).
.Range("A1:H" & lRow).AutoFilter Field:=2, Criteria1:="Shipped"
Dim rngCopy As Range
'only columns A, D:F, H
Set rngCopy = Union(.Columns("A"), .Columns("D:F"), .Columns("H"))
'filtered rows, not including header row - assumes row 1 is headers
Set rngCopy = Intersect(rngCopy, .Range("A1:H" & lRow), .Range("A1:H" & lRow).Offset(1)).SpecialCells(xlCellTypeVisible)
rngCopy.Copy
End With
wsShip.Range("B4").PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
End Sub
try the below code
Sub runthiscode()
Worksheets("Efficiency").Select
lastrow = Range("A" & Rows.Count).End(xlUp).Row
startingrow = 4
For i = 2 To lastrow
If Cells(i, 2) = "Shipped" Then
cella = Cells(i, 1)
celld = Cells(i, 4)
celle = Cells(i, 5)
cellf = Cells(i, 6)
cellh = Cells(i, 8)
Worksheets("Ship").Cells(startingrow, 2) = cella
Worksheets("Ship").Cells(startingrow, 5) = celld
Worksheets("Ship").Cells(startingrow, 6) = celle
Worksheets("Ship").Cells(startingrow, 7) = cellf
Worksheets("Ship").Cells(startingrow, 9) = cellh
startingrow = startingrow + 1
End If
Next i
End Sub

To copy number of values present in workbook1 ("A") to the work book 2 of coloumn B: VBA

I wanted to do like this:
Work Book1 (
Sheet1 has:
ColA:
AA
AA
AA
AB
AB
AB
AC
AC
AC
AC
Now I need to count how many are AA's, AB's, AC's and so on and represent their numbers in ColB of Work book B (Sheet1) like this:
ColA: ColB:
AA 3
AB 3
AC 4
Here in this second work book Col A is already mentoined so need to filter or add or change Col A just to update Col (B).
With the help of users here is the code modified so far: but I need your input thank you!
Code:
Sub foo()
Dim x As Workbook
Dim y As Workbook
'# Openning both workbooks first:
Set x = Workbooks.Open("C:\Users\ax1jk3\Desktop\Workbooks\Book2")
Set y = Workbooks.Open("C:\Users\ax1jk3\Desktop\Workbooks\Book1")
'Navigate to the first WOrkBook
Windows("Book1").Activate
'Find all the Rown in Range A that you need to copy
Sheets("Sheet1").Range("A1:A" & Sheets("Sheet1").Rows.Count).End(xlUp).Rows.Select
Selection.Copy
'Navigate to the Other WOrkBook
Windows("Book2").Activate
Sheets("Sheet2").Select
ActiveSheet.Paste
Application.CutCopyMode = False
'This will remove the Duplicates
ActiveSheet.Range("$A$1:$A$" & ActiveSheet.Rows.Count).End(xlUp).Rows.RemoveDuplicates Columns:=1, Header:=xlNo
Range("B1").Select
ActiveCell.FormulaR1C1 = "=COUNTIF([Book12]Sheet1!C1,RC[-1])"
Range("B1").Select
Selection.AutoFill Destination:=Range("B1:B" & Rows.Count).End(xlUp).Rows
Range("B1:B" & Rows.Count).End(xlUp).Rows.Select
Selection.Copy
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
Application.CutCopyMode = False
'Close x:
x.Close
End Sub
Here's how I'd do it, using the RemoveDuplicates function:
'# Opening both workbooks first:
Set x = Workbooks.Open("C:\Users\ax1jk3\Desktop\Workbooks\Book2.xlsx")
Set y = Workbooks.Open("C:\Users\ax1jk3\Desktop\Workbooks\Book1.xlsx")
'Navigate to the first WorkBook
x.Sheets(1).Activate
'Copy-Paste column A to y.sheets(1)
lastRow_x = Range("A" & Rows.Count).End(xlUp).Row
Range("A1:A" & lastRow_x).Copy
'Paste and remove duplicates
y.Sheets(1).Activate
Range("A1").PasteSpecial xlPasteValuesAndNumberFormats
Range("A:A").RemoveDuplicates Columns:=1, Header:=xlNo 'if your column has a header, use xlYes
'Count number of occurences of each row from y in x
lastRow_y = Range("A" & Rows.Count).End(xlUp).Row
For each loopCell in Range("A1:A" & lastRow_y) 'A2 if you have a header
loopCell.Offset(0, 1) = Sheets(1).Evaluate("=COUNTIF([Book2.xlsx]Sheet1!A1:A" & lastRow_x & "," & loopCell.Address & ")")
next loopCell
'Close x:
x.Close SaveChanges:=xlNo
End Sub
I haven't tested it but it should be really quick!
From my own experience, I would avoid using the .copy feature. Instead I recommend using an array to identify a unique list of items.
'# Openning both workbooks first:
Set x = Workbooks.Open("C:\Users\ax1jk3\Desktop\Workbooks\Book2")
Set y = Workbooks.Open("C:\Users\ax1jk3\Desktop\Workbooks\Book1")
'Navigate to the first WOrkBook
Windows("Book1").Activate
Sheets("Sheet1").Select
'identify end of source tab
source_ROW = ActiveSheet.UsedRange.Row - 1 + ActiveSheet.UsedRange.Rows.Count
Do While Range("A" & source_ROW) = ""
source_ROW = source_ROW - 1
Loop
source_ROW_end = source_ROW
source_ROW_start = 3
'initialize unique value array
Dim unique_ARRAY() As String
ReDim unique_ARRAY(1 To 1)
unique_ARRAY(1) = Range("A" & source_ROW_start)
'identify unique list
For source_ROW = source_ROW_start To source_ROW_end
'initialize
source_record = Range("A" & source_ROW)
new_value = "dunno_yet"
For i = 1 To UBound(unique_ARRAY, 1)
If source_record = unique_ARRAY(i) Then
'value already exists in the array
new_value = "no"
'no need to continue searching
Exit For
End If
Next i
If new_value = "no" Then
'the source_record matched values already found in the array
'does nothing
Else
'a new source_record was found
'new_value = "yes"
'redimensionalize the array while preserving pre-existing values
ReDim Preserve unique_ARRAY(1 To UBound(unique_ARRAY) + 1)
'read the new value into the new upper bound of the array
unique_ARRAY(UBound(unique_ARRAY, 1)) = source_record
End If
Next source_ROW
'Navigate to the Other WOrkBook
Windows("Book2").Activate
Sheets("Sheet2").Select
'cycle through each item in the array
for i = 1 to UBound(unique_ARRAY)
'write values to book2
Range("A" & i) = unique_ARRAY(i)
Range("B" & i) = "=COUNTIF([Book1]Sheet1!C1,RC[-1])"
'convert formulas to values
Range("B" & i).Copy
Range("B" & i).PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks:=False, Transpose:=False
next i
'Close x:
x.Close
End Sub
You may also consider adapting the array to count how many times a value appears. Then you can eliminate .PasteSpecial at the end.