If cell is empty, paste, Else, go to next blank cell and paste - excel

Basically I just need excel to verify if Cell A1 is empty.
If A1 is empty, paste starting in A1.
If A1 is not empty, go down to the next blank cell in Column A, and paste there.
I am receiving an error : Application-defined or object-defined error on the Else part of the code.
If IsEmpty("A1") Then
Range("A1").Select
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
Else
Range("A1").End(xlDown).Offset(1, 0).Select
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
End If

I noticed two issues. (1) In the first line of code, you are testing whether the text "A1" is empty, not cell A1. So first change it so that the IsEmpty tests the cell A1. (2) As you add entries below A1, you need some method of counting how many rows down to go before pasting. Right now, your code starts at cell A1 and offsets by 1. This will only work once. The sample below counts how many rows are filled in the A column and then offsets by 1 row.
If IsEmpty(Range("A1")) Then
Range("A1").Select
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
Else
Range("A" & Rows.Count).End(xlUp).Offset(1).Select
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
End If

Paste into Next Available Cell in Column
The Issues
Look at your code and imagine that cell A1 is not empty, but the rest of the cells in column A are. Then Range("A1").End(xlDown) will 'jump' to the bottom-most cell of column A: A1048576. You are additionally trying to do .Offset(1) which is not possible, hence the error.
Now again, look at your code and imagine that the range A1:A5 is not empty, but cell A6 is. Then Range("A1").End(xlDown).Offset(1, 0) will 'jump' to cell A6. But imagine that cell A7 is also not empty. Then you will possibly overwrite the value in cell A7.
Introducing a Function (A Quick Fix)
You could do:
Dim dCell As Range: Set dCell = RefFirstAvailableCell(Range("A1"))
dCell.PasteSpecial Paste:=xlPasteValues ' the rest were default values
Application.CutCopyMode = False
which uses the following function:
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Purpose: Creates a reference to the First Available Cell in a column,
' i.e. creates a reference to the cell
' below the Last Non-Empty Cell in the Column Range
' spanning from the First Cell of a range ('rg')
' to the Bottom-Most Cell of the worksheet column.
' Remarks: If all cells in the Column Range are empty,
' it creates a reference to the First Cell.
' If the Bottom-Most Cell of the worksheet column
' is not empty, it returns 'Nothing'.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function RefFirstAvailableCell( _
ByVal rg As Range) _
As Range
If rg Is Nothing Then Exit Function
With rg.Cells(1)
Dim wsrCount As Long: wsrCount = .Worksheet.Rows.Count
Dim fRow As Long: fRow = .Row
Dim lCell As Range
Set lCell = .Resize(wsrCount - fRow + 1) _
.Find("*", , xlFormulas, , , xlPrevious)
If lCell Is Nothing Then
Set RefFirstAvailableCell = .Offset
Else
Dim lRow As Long: lRow = lCell.Row
If lRow = wsrCount Then
Exit Function
Else
Set RefFirstAvailableCell = .Offset(lRow - fRow + 1)
End If
End If
End With
End Function
Testing the Function
Instead of the End statement, the function uses the Range.Find method, which is more reliable. In fact, it 'normally' (think merged cells or similar) only fails if the worksheet is filtered.
The following procedure illustrates how to copy by assignment, which is 'cleaner' and more efficient (faster) than PasteSpecial when copying values only. It also introduces some additional fail-safes (Validate...).
To utilize it, copy both codes to a standard module (e.g. Module1) of a new workbook and make sure it contains Sheet1 and Sheet2 (code names). Run the procedure and see what has happened in both worksheets. Repeat the steps a few times and observe the changes in Sheet1. Play with it by changing the various constant values in this procedure's code (not the function).
Sub RefFirstAvailableCellTEST()
' Create a reference to the Source Range.
Dim srg As Range: Set srg = Sheet2.Range("B2:D5")
' Populate the Source Range.
Dim sCell As Range
Dim n As Long
For Each sCell In srg.Areas(1).Cells
n = n + 1
sCell.Value = n
Next sCell
' Write the number of source rows and columns to variables.
Dim rCount As Long: rCount = srg.Rows.Count
Dim cCount As Long: cCount = srg.Columns.Count
' Create a reference to the Destination Initial First Cell.
Dim diCell As Range: Set diCell = Sheet1.Range("A2")
' Create a reference to the Destination First Available Cell.
Dim dCell As Range: Set dCell = RefFirstAvailableCell(diCell)
' Validate First Available Cell.
If dCell Is Nothing Then Exit Sub
If dCell.Row > Sheet1.Rows.Count - rCount + 1 Then Exit Sub
If dCell.Column > Sheet1.Columns.Count - cCount + 1 Then Exit Sub
' Create a reference to the Destination Range.
Dim drg As Range: Set drg = dCell.Resize(rCount, cCount)
' Write the values from the Source Range to the Destination Range.
drg.Value = srg.Value
End Sub

Related

Cycle value's in set cells

I'm trying to make value's cycle with VBA (or another way).
The idea is that when the button is pressed that all values go forward, and the first one is moved to the back.
The cells with a value are C4, F4, I4, L4, O4, R4, U4, X4, AA4 and AD4 (always 2 cells in between.)
Also, not all 10 cells are always in use, sometimes there are only 2 or three, but other times you also have 7 or up to all 10.
Values are inputted the first time with a drop-down menu.
This is the macro I tried to use, this one just gives error 1004 :
Sub cycle()
Range("C4").Select
Selection.Copy
Range("AG15").Select
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
Range("F4:AD4").Select
Application.CutCopyMode = False
Selection.Copy
Range("C4").Select
ActiveSheet.PasteSpecial Format:=3, Link:=1, DisplayAsIcon:=False, _
IconFileName:=False
Range("AG15").Select
Selection.Copy
Sheets("Blad1").Select
Range("C5").Select
Range(Selection, Selection.End(xlRight)).Select
Selection.Copy
Range("AG15").Select
Application.CutCopyMode = False
Selection.ClearContents
End Sub
Added a screenshot as example with 4 values. Just keep in mind this can be up to 10 values.
I already tried to just copy the value of C4 to another cell, then select F4 till AD4, copy them end paste to C4, and then copy the one that was set aside, back to the first available of those 10 cells, starting from the left, but it messed up the whole excel, so deleted that one.
Cycle Range Values
Option Explicit
Sub CycleLeft()
Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing this code
Dim ws As Worksheet: Set ws = wb.Sheets("Blad1")
Dim rg As Range: Set rg = ws.Range("C4,F4,I4,L4,O4,R4,U4,X4,AA4,AD4")
Dim currentCell As Range, previousCell As Range, CurrentValue, FirstValue
Dim IsNotFirst As Boolean
For Each currentCell In rg.Cells
CurrentValue = currentCell.Value
If Len(CStr(CurrentValue)) > 0 Then ' is not blank
If IsNotFirst Then
previousCell.Value = CurrentValue
Else
FirstValue = CurrentValue
IsNotFirst = True
End If
Set previousCell = currentCell
End If
Next currentCell
If Not previousCell Is Nothing Then previousCell.Value = FirstValue
End Sub

VBA Excel - copy a range from all files in a directory and paste into one workbook cumulative on first empty row

I use a nice code, which is here:
Copying a range from all files within a folder and pasting into master workbook
I've changed the paste data from columns to rows by providing:
shTarget.Cells(1, lRow).Offset(1, 0).PasteSpecial xlPasteValuesAndNumberFormats
instead of:
shTarget.Cells(1, lRow).PasteSpecial xlPasteValuesAndNumberFormats
and to works fine, although everything from the range is copied roughly to the same place. I would like the new data to be copied at the first empty row beneath the data copied earlier (from the first workbook in the directory).
I tried to modify my code by the example here:
https://www.mrexcel.com/board/threads/vba-paste-new-data-after-last-row.951096/
https://www.exceldemy.com/excel-vba-copy-paste-values-next-empty-row/
Copy and Paste a set range in the next empty row
by providing the offset as follows:
shTarget.Cells(1, lRow).Offset(1, 0).PasteSpecial xlPasteValuesAndNumberFormats
but it doesn't work as expected. The data is still copied to the same place several times. Eventually I have just the data from my last workbook in the directory.
My full code looks like this:
Sub CopyData(ByRef shSource As Worksheet, shTarget As Worksheet)
Const Bo As String = "A2:H100"
Dim lRow As Long
lRow = shTarget.Cells(Rows.Count, "A").End(xlUp).Row + 1
shSource.Range(Bo).Copy
shTarget.Cells(1, lRow).Offset(1, 0).PasteSpecial xlPasteValuesAndNumberFormats
Application.CutCopyMode = xlCopy
End Sub
If I change the
lRow = shTarget.Cells(Rows.Count, "A").End(xlUp).Row + 1
to
lRow = shTarget.Cells(Rows.Count, "A").End(xlUp).Offset(1)
then I've got an error:
Application defined or object defined error
Is there any way of copying the data on a cumulative basis? I.e. Data from first workbook regardless the range provided (A2:A100) occupies range just A2:A10 and consecutively the data from the 2nd workbook is copied to range A11:A30 and so forth?
Copy Data Using a Method
A Quick Fix: Using the End Property (not recommended)
Sub CopyDataQF(ByVal shSource As Worksheet, ByVal shTarget As Worksheet)
Const Bo As String = "A2:H100"
Dim FirstRow As Long
FirstRow = shTarget.Cells(shTarget.Rows.Count, "A").End(xlUp).Row + 1
shSource.Range(Bo).Copy
shTarget.Cells(FirstRow, "A").PasteSpecial xlPasteValuesAndNumberFormats
Application.CutCopyMode = False
End Sub
An Improvement: Using the Find Method
Sub CopyData(ByVal shSource As Worksheet, ByVal shTarget As Worksheet)
' Define constants.
Const SRC_RANGE As String = "A2:H100"
Const TGT_FIRST_CELL As String = "A2"
' Reference the Source range.
Dim srg As Range: Set srg = shSource.Range(SRC_RANGE)
' Reference the given first Target cell.
If shTarget.FilterMode Then shTarget.ShowAllData
Dim tfCell As Range: Set tfCell = shTarget.Range(TGT_FIRST_CELL)
' Reference the first available Target cell, the cell in the same column
' but in the row below the bottom-most non-empty row.
With tfCell
Dim tlCell As Range
Set tlCell = .Resize(shTarget.Rows.Count - .Row + 1, _
shTarget.Columns.Count - .Column + 1) _
.Find("*", , xlFormulas, , xlByRows, xlPrevious)
If Not tlCell Is Nothing Then
Set tfCell = shTarget.Cells(tlCell.Row + 1, tfCell.Column)
End If
End With
' Copy.
srg.Copy
tfCell.PasteSpecial xlPasteValuesAndNumberFormats
Application.CutCopyMode = False
End Sub

Delete cells in column after last row in another

I would like to clear content of cells (not delete rows) in a column after the last row of another column. The code would act as follows to work properly
Go to last cell in column BA,
move to the right to column BB
delete all rows in BB below that last rows
When I try recording the macro the code includes the range of that last cell as a fixed place.
This is the code, I highlighted where I believe the issue is
Sub CopyPaste2()
'
' CopyPaste2 Macro
'
'
Columns("AS:AV").Select
Selection.Copy
Columns("AX:AX").Select
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
**Range("BA7").Select
Selection.End(xlDown).Select
Range("BB47").Select
Range(Selection, Selection.End(xlDown)).Select
Application.CutCopyMode = False
Selection.ClearContents**
Range("BB46").Select
Selection.End(xlUp).Select
Range("BB7").Select
Selection.AutoFill Destination:=Range("BB7:BB46")
Range("BB7:BB46").Select
Range("BA6").Select
ActiveWorkbook.Worksheets("KPI - Efficiency - Case Level").Sort.SortFields. _
Clear
ActiveWorkbook.Worksheets("KPI - Efficiency - Case Level").Sort.SortFields.Add _
Key:=Range("BA7:BA46"), SortOn:=xlSortOnValues, Order:=xlAscending, _
DataOption:=xlSortNormal
With ActiveWorkbook.Worksheets("KPI - Efficiency - Case Level").Sort
.SetRange Range("AX6:BB46")
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
End Sub
Im pretty new to VBA so really appreciate your help
Try this:
Add the following line near the top of your code - traditionally, we tend to declare our variables at the start of a procedure:
'declare 'lastrow' to store value of row number
Dim lastrow As Long
And then at the end of your code, after the sort etc., add this:
With ActiveWorkbook.Worksheets("KPI - Efficiency - Case Level")
' find last used row of column BA and add 1
lastrow = .Range("BA" & .Rows.Count).End(xlUp).Row + 1
' clear from 'lastrow' to bottom of sheet in column BB
.Range("BB" & lastrow & ":BB" & .Rows.Count).ClearContents
End With
I can see you've recorded this macro, so it's a little messy. If you're interested in learning how to craft better vba that is more portable and easier to read, you will want to read up on avoiding Select etc.:
How to avoid using Select in Excel VBA
Clear the Cells Below a Range
If rg is a range object, to clear all cells below it, you can use the following line:
rg.Resize(rg.Worksheet.Rows.Count - rg.Row - rg.Rows.Count + 1).Offset(rg.Rows.Count).Clear
In the code, some parts of it are replaced with variables:
drg.Resize(ws.Rows.Count - FirstRow - rCount + 1).Offset(rCount).Clear
If rg has only one row, you can simplify with:
rg.Resize(rg.Worksheet.Rows.Count - rg.Row).Offset(1).Clear
Clear Below
Sub ClearBelow()
' Reference the workbook ('wb').
Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing this code
' Reference the worksheet ('ws').
Dim ws As Worksheet: Set ws = wb.Worksheets(wsName)
Dim lCell As Range
' ("Go to last cell in column BA")
' Reference the last non-empty cell in column 'BA' using 'End'
' (in the code the Find method is used instead of the End property).
Set lCell = ws.Cells(ws.Rows.Count, "BA").End(xlUp)
' ("Move to the right to column BB")
' Reference the cell adjacent to the right using offset.
Set lCell = lCell.Offset(, 1)
' Reference the cell in the same row but in column 'BB' using 'EntireRow'.
' (can be any column).
'Set lCell = lCell.EntireRow.Columns("BB")
' ("Delete all rows in BB below that last rows")
' Clear all cells below the cell using 'Resize' and 'Offset'.
lCell.Resize(ws.Rows.Count - lCell.Row).Offset(1).Clear
End Sub
The Code
Option Explicit
Sub CopyPaste2() ' be more creative e.g. 'CreateEfficiencyReport'!
' Define constants.
Const wsName As String = "KPI - Efficiency - Case Level"
Const sColumnsString As String = "AS:AV" ' Source Copy Columns
Const dFirstColumnString As String = "AX" ' Destination First Copy Column
Const FirstRow As Long = 7
' Reference the workbook ('wb').
Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing this code
' Reference the worksheet ('ws').
Dim ws As Worksheet: Set ws = wb.Worksheets(wsName)
' To make sure that the worksheet is not filtered, when the remaining
' code would fail, you could use the following:
'If ws.FilterMode Then ws.ShowAllData
' Reference the source columns range ('scrg') ('$AS$7:$AV$1048576').
Dim scrg As Range: Set scrg = ws.Rows(FirstRow).Columns(sColumnsString) _
.Resize(ws.Rows.Count - FirstRow + 1)
'Debug.Print scrg.Address(0, 0)
' Attempt to reference the last cell ('lCell'), the bottom-most
' non-empty cell in the source columns range (for the bottom-most
' non-blank cell, use 'xlValues' instead of 'xlFormulas').
Dim lCell As Range
Set lCell = scrg.Find("*", , xlFormulas, , xlByRows, xlPrevious)
If lCell Is Nothing Then Exit Sub ' no data
'Debug.Print lCell.Address(0, 0)
' Reference the source range ('srg').
Dim srg As Range: Set srg = scrg.Resize(lCell.Row - FirstRow + 1)
'Debug.Print srg.Address(0, 0)
' Write the number of rows and columns of the source range
' to variables ('rCount', 'cCount').
Dim rCount As Long: rCount = srg.Rows.Count
Dim cCount As Long: cCount = srg.Columns.Count
Dim dcrg As Range ' Destination Copy Range
Dim dfcCell As Range ' Destination First Copy Cell
' Reference the destination first copy cell ('dfcCell').
Set dfcCell = ws.Cells(FirstRow, dFirstColumnString)
' Reference the destination copy range ('dcrg').
Set dcrg = dfcCell.Resize(rCount, cCount)
'Debug.Print dcrg.Address(0, 0)
' Copy the values from the source range to the destination copy range.
dcrg.Value = srg.Value
Dim dfrg As Range ' Destination Formula Range
Dim dffCell As Range ' Destination First Formula Cell
' Reference the destination first formula cell ('dffCell')
' in the column adjacent to the right of the copy range.
Set dffCell = dfcCell.Offset(, cCount)
' Reference the destination formula range ('dfrg').
Set dfrg = dffCell.Resize(rCount)
'Debug.Print dfrg.Address(0, 0)
Dim drg As Range ' (Whole) Destination Range
If rCount > 1 Then
' Write the formula from the first formula cell to the remaining cells
' of the destination formula range.
dfrg.Formula = dffCell.Formula
'
' Reference the destination range ('drg').
Set drg = dcrg.Resize(, cCount + 1) ' include the formula column
'Debug.Print drg.Address(0, 0)
' Sort the destination range ('drg') by the last column
' of the copy range.
drg.Sort drg.Columns(cCount), xlAscending, , , , , , xlNo
'Else ' there is only one row of data; do nothing
End If
' Clear the cells below the destination range.
drg.Resize(ws.Rows.Count - FirstRow - rCount + 1).Offset(rCount).Clear
End Sub

VBA to copy paste ranges based on not having 0

I have a set of values in range L10:M34 and I'd like to select all L:M where M doesn't have value 0. Then I want to paste whole selection as values without blacks to U5. I am able to do that manually, but I'd like to use VBA, and repeat this to 2 other sets of locations in same worksheet.
What I have is only selecting whole range to copy, and I'd like to exclude rows where M has 0.
Range("L10:M34").Select
Selection.Copy
Range("U5").Select
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
If I understand you correctly :
Sub test()
Dim rg As Range: Dim dest As Range
Dim i As Long: Dim col As Long
Dim rgNonZero As Range: Dim r As Range
Set rg = Range("L10:M34")
i = Range(Split(rg.Address(0, 0), ":")(0)).Row
col = Range(Split(rg.Address(0, 0), ":")(1)).Column
Do Until Cells(i, col) <> 0: i = i + 1: Loop
Set rgNonZero = Range(Cells(i, col - 1), Cells(i, col))
For Each r In rg.Rows
If r.Columns(2).Value <> 0 Then Set rgNonZero = Union(rgNonZero, r)
Next
rgNonZero.Copy
Range("U5").PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=True
End Sub
First the sub create the data range in L10:M34 as rg variable.
Then get the first row defined in rg into i variable.
Then get the column (which has the zero and non zero values) number into col variable.
Then it loop (sorry difficult for me to explain in English) until it find the first row of column M which has value non-zero, then set the range of this row as rgNonZero variable.
Then it loop each row in rg as r variable, and check if this row column(2) value is not zero - then it add the range of r into rgNonZero.
After the loop done, it copy rgNonZero and paste tranpose to cell U5.

Copy and Paste one Cell at a time from a list to another sheet

i am a novice when it comes to VBA and would like some help.
I am trying to copy one cell at a time from one sheet to to another. The reason for this is because I want to copy one cell (account #) from a list (sheet "List") and paste into a predefined cell is another sheet ("Analysis") and run code that will extract data from a program. i want to then repeat this process for all the account #s in that list until the list ends. The # of accounts in this list will change periodically. Account # will always be entered into Cell "F2"
The code i am using to extract data is,
Range("F2").Select
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
Application.Run "'Option holding.xls'!SecurityDistribution"
Loop through the list and call the macro
Sub Do_It()
Dim Sh As Worksheet, ws As Worksheet
Dim Rng As Range, LstRw As Long
Dim F1 As Range, c As Range
Set Sh = Sheets("Transaction Analysis")
Set F1 = Sh.Range("F1")
Set ws = Sheets("List")
With ws
LstRw = .Cells(.Rows.Count, "A").End(xlUp).Row
Set Rng = .Range("A2:A" & LstRw)
For Each c In Rng.Cells
F1.Value = c
MsgBox "Call Macro Here"
Next c
End With
End Sub

Resources