only paste (or copy) rows with values - excel

I'm trying to copy a range to the first empty row on the next sheet.
But sometimes there is only 2 rows with values and other times there are 5,6 or 7 rows.
I have this for now:
Private Sub test()
Application.ScreenUpdating = False
Dim copySheet As Worksheet
Dim pasteSheet As Worksheet
Set copySheet = Worksheets("Blad1")
Set pasteSheet = Worksheets("Games")
copySheet.Range("AG4:AS13").Copy
pasteSheet.Cells(Rows.Count, 1).End(xlUp).Offset(1, 0).PasteSpecial xlPasteValues
Application.CutCopyMode = False
Application.ScreenUpdating = True
End Sub
It works to get it on the next sheet, but it seems to always take the empty cells that were pasted earlier into account when pasting.
So the first time I click it, I get say 2 filled rows, and 6 empty rows pasted, the next time, it pastes the 2 full rows on the 9th row instead of on the 3rd row.
The empty rows on sheet 1 do hold formulas, but they are not present in the destination cells after pasting.
It does do it's jobs when I select those empty cells, press 'delete', and the next time I use the macro, it does paste it on the 3rd row.
Any ideas how to solve this?
Tried looking up a solution, but nothing that really worked. I might have been searching in the wrong direction, so that's why I came here.

Copy Values (Empty vs Blank)
You have copied blank cells which are not empty and End(xlUp) detects them.
Before using the following, which will not copy blank cells, select the range from the first row below the last row with data to the bottom of the worksheet and press Del to get rid of any blank cells.
Sub CopyValues()
Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing this code
Dim sws As Worksheet: Set sws = wb.Sheets("Blad1")
Dim srg As Range: Set srg = sws.Range("AG4:AS13")
Dim scCount As Long: scCount = srg.Columns.Count
Dim dws As Worksheet: Set dws = wb.Sheets("Games")
Dim dfCell As Range: Set dfCell = dws.Range("A2")
Dim dlCell As Range
With dfCell
With .Resize(dws.Rows.Count - .Row + 1, dws.Columns.Count - .Column + 1)
Set dlCell = .Find("*", , xlFormulas, , xlByRows, xlPrevious)
End With
If Not dlCell Is Nothing Then
Set dfCell = .Offset(dlCell.Row - .Row + 1)
End If
End With
Dim drrg As Range: Set drrg = dfCell.Resize(, scCount)
Dim srrg As Range
For Each srrg In srg.Rows
If Application.CountBlank(srrg) < scCount Then
drrg.Value = srrg.Value
Set drrg = drrg.Offset(1)
End If
Next srrg
MsgBox "Values copied.", vbInformation
End Sub

Related

How do I copy a specific range of cells after I use AutoFilter in VBA?

I am having a little trouble figuring out how the AutoFilter function works in VBA. This line of code:
Worksheets("my sheet").Range("A1").AutoFilter Field:=14, Criteria1:="my criteria" filters the worksheet in col 14 just fine like I want, but when I go to copy the first col using this snippet of code:
Dim LR As Long
LR = Cells(Rows.Count, 1).End(xlUp).Row
Worksheets("my sheet").Range("A2:A" & LR).Copy
I then have the first header of the first col (A1) copied into my sheet I created, which is not what I want. I want to copy everything that is filtered BUT the header (A2 and down to the end of the filtered col).
I tried to separate the functions that create the sheets that I am copying the col's into, in case there was some issue when the sheets were being created, but that did not fix the issue. I did snag this snippet of code from the internet:
Dim LR As Long
LR = Cells(Rows.Count, 1).End(xlUp).Row
But to my knowledge, that shouldn't be the issue, because my code does work if I run the macro again (twice), once the new sheets have been created and they have been formatted (just some text in cells A1 and B1 and some formatting of those cells). Any insight would be appreciated!
Set the range of filtered data
Set Rng = Range("A1:A" & Cells(Rows.Count, "A").End(xlUp).Row).SpecialCells(xlCellTypeVisible)
Copy First Column of Filtered Range
In the Immediate window Ctrl+G, see the relevant addresses of the ranges.
Option Explicit
Sub CopyFirstColumnOfFilteredRange()
Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing this code
Dim sws As Worksheet: Set sws = wb.Sheets("Sheet1")
If sws.AutoFilterMode Then sws.AutoFilterMode = False ' turn off AutoFilter
Dim srg As Range: Set srg = sws.Range("A1").CurrentRegion ' table range
Dim sdrg As Range: ' data range (no headers): first column ', 1'
Set sdrg = srg.Resize(srg.Rows.Count - 1, 1).Offset(1)
Debug.Print srg.Address
Debug.Print sdrg.Address
srg.AutoFilter 14, "My Criteria"
Dim sdvrg As Range ' visible data range (no headers)
On Error Resume Next ' to prevent error if no cells
Set sdvrg = sdrg.SpecialCells(xlCellTypeVisible)
On Error GoTo 0
sws.AutoFilterMode = False ' turn off AutoFilter
If sdvrg Is Nothing Then
MsgBox "No filtered rows.", vbExclamation
Exit Sub
End If
Debug.Print sdvrg.Address
Dim dws As Worksheet: Set dws = wb.Sheets("Sheet2")
Dim dfCell As Range: Set dfCell = dws.Range("A2")
sdvrg.Copy dfCell
MsgBox "Filtered column copied.", vbInformation
End Sub

To fill a column following one condition (when cell is empty)

I am trying to find out how to fill a cell on the next empty row, using a loop.
I learned how to loop through a range, and how to fill cells conditionally, for example:
Dim column As Range: Set column = Sheets(1).UsedRange.Columns(1)
For Each cell In column.Cells
If cell.Value >= 0 Then
cell.Offset(0, 1).Value = "Positive"
ElseIf cell.Value < 0 Then
cell.Offset(0, 1).Value = "Negative"
End If
Next
However, my current task is a bit more complex, and I do not know how to solve a particular issue. I have two sheets, one in each workbook. If, when looping through a column in the first workbook, I find an empty cell, then the value in the cell that is 0,1 offset to it should be copied to a column in the second workbook.
My objective is thus that the second workbook contains a tidy column, with one value after the other row by row. The first workbook remains unchanged.
The particular issue is that I can't find what the exact syntax or condition to tell Excel to fill a cell on the first empty row that it finds.
This is what I have so far:
Dim wb As Workbook: Set wb = Workbooks("QuartalReport.xlsm")
Dim ws As Worksheet: Set ws = wb.Worksheets(1)
Dim column As Range: Set column = ws.Sheets(1).UsedRange.Columns(1) 'some cells in this column are empty
Dim wb2 As Workbook: Set wb2 = Workbooks("ClientList.xlsm")
Dim ws2 As Worksheet: Set ws2 = wb2.Worksheets(1)
Dim column2 As Range: Set column = ws2.Sheets(1).Columns(3) 'this column will be filled as the macro is used each time
For Each cell In column.Cells
If cell.Value = "" Then
???
Edited for clarification:
This code will go in the first workbook ("QuartalReport.xlsm"). Both workbooks have only one sheet.
The data in the first workbook has no table formatting, and it starts in row 3.
The data in the second workbook should begin with cell C2 (or any column in row 2), as the first row will be for the header (though like in the first workbook, it is unformatted). Save for this column, the worksheet will be empty.
The objective is to copy the value in the cell in column B of the first workbook if the cell in the same row in column A is empty. For example if cells A3 through A5 in "QuartalReport.xlsm contain values, the rows should altogether be skipped. But if A6 is blank, then the value of whatever is in B6 should be copied to the next empty row (being the first case, C2) in "ClientList.xlsm". If the next empty cell is in A12, then B12 should be copied to C3. As such the column in the second workbook will have no empty rows between data.
A VBA Lookup: Copy Matches Consecutively
Option Explicit
Sub CopyMatchesConsecutively()
' Reference the source range ('srg').
Dim swb As Workbook: Set swb = ThisWorkbook
Dim sws As Worksheet: Set sws = swb.Worksheets(1)
Dim sfrrg As Range: Set sfrrg = sws.Range("A3:B3") ' first row range
Dim srg As Range, srCount As Long
With sfrrg
' '.Resize(sws.Rows.Count - .Row + 1)' is 'A3:B1048576' (or 'A3:B65536')
Dim slCell As Range: Set slCell = .Resize(sws.Rows.Count - .Row + 1) _
.Find("*", , xlFormulas, , xlByRows, xlPrevious)
If slCell Is Nothing Then Exit Sub ' no data
srCount = slCell.Row - .Row + 1
Set srg = .Resize(srCount)
End With
' Write the values from the source range to an array ('Data').
Dim Data(): Data = srg.Value
' Write the results to the top of the first column of the array.
Dim sr As Long, dr As Long
For sr = 1 To srCount
If IsEmpty(Data(sr, 1)) Then ' empty
' Or:
'If Len(CStr(Data(sr, 1))) = 0 Then ' blank e.g. '=""' (includes empty)
dr = dr + 1
Data(dr, 1) = Data(sr, 2)
End If
Next sr
' The results are in the rows from 1 to 'dr' of the first column.
' Reference the destination range ('drg').
Dim dwb As Workbook: Set dwb = Workbooks("ClientList.xlsm")
Dim dws As Worksheet: Set dws = dwb.Worksheets(1)
Dim dfCell As Range: Set dfCell = dws.Range("C2")
Dim drg As Range: Set drg = dfCell.Resize(dr) ' single-column of size 'dr'
' Write the results to the destination range.
drg.Value = Data ' write
drg.Resize(dws.Rows.Count - drg.Row - dr + 1).Offset(dr).Clear ' clear below
' Inform.
MsgBox "Matches copied consecutively.", vbInformation
End Sub

Copy and paste data from another excel file (each tab)

I am trying to copy columns f:g from each tab in a file (wb). Each tab has a different amount of rows so I also need to include a ctrl+shift+down when selecting the range. When pasting into my current file (ws) I also need to consider an offset because I am pasting 2 columns each time (next to each other).
I tried the following code but I keep getting a Run time error (object doesn't support this property), what am I missing?
For i = 1 To wb.Sheets.Count
wb.Range("f2:G2").End(xlDown).Select.Copy
start.Offset(i + 2, 2).PasteSpecial xlPasteValues
Next i
Copy Values From All Worksheets
Sub Test()
' Before your code...
Const sFirstRowAddress As String = "F2:G2"
' First part of your code...
Dim wb As Workbook ' Set wb = ?
Dim Start As Range ' Set Start = ?
' New code...
' Using the first source worksheet, calculate the total number of rows
' ('trCount') and the number of columns ('cCount').
Dim trCount As Long
Dim cCount As Long
With wb.Worksheets(1).Range(sFirstRowAddress)
trCount = .Worksheet.Rows.Count - .Row + 1
cCount = .Columns.Count
End With
' Reference the first destination row ('drrg').
Dim drrg As Range: Set drrg = Start.Cells(1).Resize(, cCount)
Dim sws As Worksheet
Dim srg As Range
Dim slCell As Range
Dim drg As Range
Dim rCount As Long
For Each sws In wb.Worksheets
' Turn off AutoFilter.
If sws.AutoFilterMode Then sws.AutoFilterMode = False
' Reference the first source row...
With sws.Range(sFirstRowAddress)
' Attempt to reference the last non-empty cell ('slCell').
Set slCell = .Resize(trCount) _
.Find("*", , xlFormulas, , xlByRows, xlPrevious)
If Not slCell Is Nothing Then ' a non-empty cell was found
rCount = slCell.Row - .Row + 1
Set srg = .Resize(rCount)
Set drg = drrg.Resize(rCount)
drg.Value = srg.Value ' copy values
Set drrg = drrg.Offset(rCount) ' next first destination row
'Else ' all source cells are empty; do nothing
End If
End With
Next sws
' The remainder of your code...
End Sub

Transfer data from master sheet using offset from reference cell

I am trying to transfer data from one master sheet to multiple template sheets based on cell name match with sheet name of the template sheets using a specific offset in the master sheet. However the referencing does not seem to work. In my case sheet named "Combine" is the master sheet. The offset value based on match cellname is 6 columns away from the matched cell. I am getting debugging error. Can anyone fix the problem?
Sub Button5_Click()
Dim wkSht As Worksheet, wsC As Worksheet, rngSearch As Range
Dim shNCell As Range
Set wsC = Sheets("Combine")
Set rngSearch = wsC.Range("A4:A800")
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("F12").Resize(19, 1).Value = wsC.Range(shNCell.Offset(0, 6)).Value
End If
Next wkSht
End Sub
Lookup Values (VBA)
Option Explicit
Sub Button5_Click()
Const ExceptionsList As String = "Combine" ' comma-saparated, no spaces!
Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing this code
' Reference the source column range ('srg').
Dim sws As Worksheet: Set sws = wb.Worksheets("Combine")
Dim slRow As Long: slRow = sws.Cells(sws.Rows.Count, "A").End(xlUp).Row
If slRow < 4 Then Exit Sub
Dim srg As Range
Set srg = sws.Range(sws.Cells(4, "A"), sws.Cells(slRow, "A"))
' Write the names from the list (string) to an array ('Exceptions').
Dim Exceptions() As String: Exceptions = Split(ExceptionsList, ",")
Dim sCell As Range
Dim dws As Worksheet
For Each dws In wb.Worksheets
' Check if not in list.
If IsError(Application.Match(dws.Name, Exceptions, 0)) Then
' '.Cells(.Cells.Count)' ensures the search starts with
' the first cell (irrelevant in this case but good to know).
' Think: After the last cell comes the first cell.
' Using 'xlFormulas' will allow you to find even if the cell
' is in a hidden row or column. The 'formula' and the 'value'
' are the same since 'xlWhole' is used.
' 'False' is the default value of the `MatchCase` argument.
With srg
Set sCell = .Find(What:=dws.Name, After:=.Cells(.Cells.Count), _
LookIn:=xlFormulas, LookAt:=xlWhole)
End With
If Not sCell Is Nothing Then
dws.Range("F12").Value = sCell.EntireRow.Columns("G").Value
'or
'dws.Range("F12").Value = sCell.Offset(, 6).Value
'Else ' no cell found; do nothing
End If
'Else ' is in the exceptions list; do nothing
End If
Next dws
End Sub

Assistance with the (XLtoright) function. VBA

I want to paste a column selection (a2:a200) to the next available column to the right.
I am having trouble doing this.
Here I am copying the data from Tables and pasting it in the last available column to the right.
Sub CopyMonthData() Worksheets("Tables").Range("d2:d200").Copy _
Worksheets("SalesInvoicesMonth").Range("a" & Columns.Count).End(xlToLeft).Offset(1, 0) End Sub
How can I make this work?
You could use UsedRange instead.
Sub CopyMonthData()
Dim iLastCol as Integer
With Worksheets("SalesInvoicesMonth")
iLastCol = .UsedRange.Column + .UsedRange.Columns.Count - 1
Worksheets("Tables").Range("d2:d200").Copy .Cells(1,iLastCol + 1)
End With
End Sub
Declaring iLastCol helps you understand how to calculate the value but the variable is not required. You could replace it in the copy function by its actual value --> - 1 and + 1 cancel each other, so you get .Cells(1,.usedRange.Column + .UsedRange.Columns.Count) as destination range.
Copy to the Next Available Column
A Quick Fix (End)
I'm assuming that you want to copy the column range D2:D200 in the source worksheet (Tables) to the second row of the column after (next to) the last non-empty column of the destination worksheet (SalesInvoicesMonth) and that both worksheets reside in the workbook containing this code (ThisWorkbook). Also, I'm assuming that all columns in the destination worksheet have headers in the first row, which will be the row used to calculate the column number.
Sub CopyMonthDataSimple()
Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing this code
Dim sws As Worksheet: Set sws = wb.Worksheets("Tables")
Dim dws As Worksheet: Set dws = wb.Worksheets("SalesInvoicesMonth")
sws.Range("D2:D200").Copy _
dws.Cells(1, dws.Columns.Count).End(xlToLeft).Offset(1, 1)
End Sub
The first 1 in .Offset(1, 1) means the second row, while the second 1 means the column next to the column of the last (not hidden) header cell.
A More Flexible Solution (Find)
The following will copy the column range from the given cell (sFirst) to the bottom-most non-empty cell of the column of the source worksheet to the destination worksheet starting with the cell in the given row (dfrow) of the column after the last non-empty column.
Adjust (play with) the values in the constants section.
Sub CopyMonthData()
' Constants & Workbook
' Source
Const sName As String = "Tables"
Const sFirst As String = "D2"
' Destination
Const dName As String = "SalesInvoicesMonth"
Const dfRow As Long = 2
' Workbook
Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing this code
' Attempt to create a reference to the Source Column Range ('srg').
Dim sws As Worksheet: Set sws = wb.Worksheets(sName)
Dim sfCell As Range: Set sfCell = sws.Range(sFirst)
Dim sfOffset As Long: sfOffset = sfCell.Row - 1
Dim slCell As Range
' The bottom-most non-empty cell in the Source Column.
Set slCell = sfCell.Resize(sws.Rows.Count - sfOffset) _
.Find("*", , xlFormulas, , , xlPrevious)
If slCell Is Nothing Then
MsgBox "The range from the 'First Source Cell' to the bottom-most " _
& "cell of the 'Source Column' is empty.", _
vbCritical, "CopyMonthData"
Exit Sub
End If
' The following...
Dim srCount As Long: srCount = slCell.Row - sfOffset
Dim srg As Range: Set srg = sfCell.Resize(srCount)
' ... is another way of doing...
'Dim srg As Range: Set srg = sws.Range(sfCell, slCell)
' Attempt to create a reference
' to the Destination First Cell Range ('dfCell').
Dim dws As Worksheet: Set dws = wb.Worksheets(dName)
Dim dlCell As Range
' The bottom-most non-empty cell in the last non-empty column.
Set dlCell = dws.Cells.Find("*", , xlFormulas, , xlByColumns, xlPrevious)
Dim dCol As Long
If dlCell Is Nothing Then ' The Destination Worksheet is empty.
dCol = 1
Else
If dlCell.Column = dws.Columns.Count Then
MsgBox "The 'Destination Last Column' is not empty.", _
vbCritical, "CopyMonthData"
Exit Sub
Else
dCol = dlCell.Column + 1
End If
End If
Dim dfCell As Range: Set dfCell = dws.Cells(dfRow, dCol)
' Copy
' Copy values, formats and formulas.
srg.Copy dfCell
' Or copy values (only) by assignment (more efficient (faster)).
'dfCell.Resize(srCount).Value = srg.Value
dfCell.EntireColumn.AutoFit
End Sub

Resources