I am trying to copy a fixed column range (D20:D39) into an empty column, say K20:K39. The columns keeps getting updated every time I update (D20:D39), i.e. K, L, M, N... will get updated when I want to copy (D20:D39) over. I am not proficient in coding or computer science and just trying to make my spreadsheet efficient. Any one able to help?
I've looked at some other threads but mostly mentioned copying into a separate worksheet. None mentioned this "rolling" copy n paste mechanism.
The following will calculate the last column used (in row 20, since we'll be copying to there) then will set the column values one column over to the same ones as range D20 to D39:
Sub coppercopy()
Dim lastcol As Integer
lastcol = Sheets("Sheet1").Cells(20, Columns.Count).End(xlToLeft).Column
Range(Cells(20, lastcol + 1), Cells(39, lastcol + 1)).Value = Range("D20:D39").Value
Range(Cells(42, lastcol + 1), Cells(32, lastcol + 1)).Value = Range("D42:D62").Value
End Sub
If you want to copy and paste instead of setting the values to be the same as the range, use this:
Sub coppercopy()
Dim lastcol As Integer
lastcol = Sheets("Sheet1").Cells(20, Columns.Count).End(xlToLeft).Column
Range("D20:D39").copy
Cells(20, lastcol + 1).PasteSpecial xlPasteValues
End Sub
Edit, the first option is edited to include more ranges. This can be done on the same line, but for code readability and ease of use I've put it on a new line. Please note that the Cells( statement always takes an R1C1 notation, unlike the Range( statement. Meaning it references to Cells(Row number, Column number). Keeping this in mind this can be easily updated to include any range to be copied as well by updating to the correct cell reference.
Second edit:
As per the comments, the range needs to be more dynamic while still getting rid of the sum formula when copying. The following sub is adapted to this. It will copy the entire range starting from D20 to the last filled row, and copies it into the next available column as needed. It then loops over the entire copied range +1 (This is to account for a sum formula at the end of the range which needs to be deleted) and if blanks are found (indicating a new "paragraph") it deletes the cell above (always the sum formula for the last paragraph).
Sub coppercopy()
Dim lastcol As Integer, lastr As Integer, cel As Range
lastcol = Sheets("Sheet1").Cells(20, Columns.Count).End(xlToLeft).Column 'determine last column with values
lastr = Sheets("Sheet1").Range("D" & Rows.Count).End(xlUp).Row 'determine last row with values
Range(Cells(20, lastcol + 1), Cells(lastr, lastcol + 1)).Value = Range("D20:D" & lastr).Value 'copy range to column after last used one
For Each cel In Range(Cells(20, lastcol + 1), Cells(lastr + 1, lastcol + 1)) 'If the copied range does not end with a sum formula, omit the `+ 1` after `lastr`.
If cel.Value = "" Then 'loops over all cells and checks for blanks
cel.Offset(-1, 0).Value = "" 'if blank is found, delete cell above
End If
Next cel
End Sub
Related
I am trying to paste a selection of rows to a new sheet.
It worked previously, but now run-time error 1004 tells me that I can't paste because the copy area and paste area aren't the same size.
When I attempt to run the code, I am sure to have the A1 cell of the new sheet selected.
When I debug, it takes me to the ActiveSheet.Paste line.
Sub exportconditionstarttoend()
Dim rownum As Long
Dim colnum As Long
Dim startrow As Long
Dim endrow As Long
Dim lastrow As Long
rownum = 1
colnum = 1
lastrow = Worksheets("ETM ETM0007").Range("W63000").End(xlUp).Row
With ActiveWorkbook.Worksheets("ETM ETM0007").Range("W1:W" & lastrow)
For rownum = 1 To lastrow
Do
If .Cells(rownum, 1).Value = "Condition 1 - Price 0.25" Then
startrow = rownum
End If
rownum = rownum + 1
If (rownum > lastrow) Then Exit For
Loop Until .Cells(rownum, 1).Value = "Condition 1 - Price 0.25 - End"
endrow = rownum
rownum = rownum + 1
Worksheets("ETM ETM0007").Range(startrow & ":" & endrow).Copy
Sheets("Result").Select
Range("W1").Select
ActiveSheet.Paste
Next
End With
End Sub
You are getting the error because you are copying every column and then trying to paste every column starting at cell W1. You are trying to paste 16,384 columns into a range that only has 16,362 columns. This leaves you with a 22 column deficit and thus the paste size error.
If you are copying every column, you always need to paste on Column A.
Similarly, if you are copying every row, you always need to paste on Row 1.
i.e. change Range("W1").Select to Range("A1").Select. Note you don't need to .Select a range to copy or paste it - see this post for reasoning and best practice.
Do you really need to copy every column?
The best solution is to limit the number of columns you need to copy either by hard coding the column range or dynamically defining the column range.
"I am sure to have the A1 cell of the new sheet selected" - from your code Range("W1").Select looks like your last selected cell is W1, so you can't paste a whole row there. The solution is to change W1 to A1 or select not the entire row but the range of the table, for example,
Worksheets ("ETM ETM0007"). Range ("A" & Startrow & ":" & "I" & Endrow) .copy or any other col/row range copy method.
I am trying to copy a formula from C2 down to the last cell in the active row range (columns A and B count) then copy that same formula to every column in the active column range (row 1 count)
IE copy formula all the way down then all the way across....
Can you dim both last row and last column in the same Subroutine?
If so, how do I do this? I tried with two subroutines and failed miserably.
Sub ImportSub2()
Dim LastRowColumnA As Long
LastRowColumnA = Cells(Rows.Count, 1).End(xlUp).Row
Range("C2:C" & LastRowColumnA).Formula = "=$A2&C$1"
Call ImportSub3
End Sub
Sub ImportSub3()
Dim lastcolumn As Long
lastcolumn = Cells(Columns.Count, 1).End(xlUp).Column
Range("C2:C" & lastcolumn).Formula = "=$A2&C$1"
End Sub
Yes, this can be done; your first problem above is that you are not finding the last column correctly.
VBA formulas, especially with a mix of relative and absolute references, work better with R1C1 format. This code directly writes the required formula into the cells of interest:
Sub InjectFormula()
Dim LastRow As Long, LastCol As Long
LastRow = Cells(Rows.Count, 1).End(xlUp).Row
LastCol = Cells(1, Columns.Count).End(xlToLeft).Column
Range(Range("C2"), Cells(LastRow, LastCol)).FormulaR1C1 = "=RC1&R1C"
End Sub
If you want to copy a pre-existing formula from C2, you can instead use
Range(Range("C2"), Cells(LastRow, LastCol)).FormulaR1C1 = Range("C2").FormulaR1C1
I filled B2:GQ244 with formulae, copied the range and pasted by value before sorting the range column by column. The cells in B8:GQ244 were all blanks. Then, I wanted to concatenate the non-blank cells column by column, starting from row 2. To do so, I needed to find the last non-blank cell in each column.
For some reason, both End(xlUp) and End(xlDown) gave row 244, which was empty. I can't figure out why. I thought the file might be corrupted. So, I copied the two sheets and the module to a newly created workbook to no avail. Any explanation why both End(xlUp) and End(xlDown) gave row 244?
.Range("B2:GQ244").Formula = "=IF(ISERROR(FIND( B$1,Sheet9!$H34)),"""",Sheet9!$I34)"
'paste by value to get rid of formulae
.Range("B2:GQ244").Copy
.Range("B2").PasteSpecial Paste:=xlPasteValues
'sort by column
Dim last_row As Long
Dim j As Long
For i = 2 To 200 Step 1
Range(.Cells(2, i), .Cells(245, i)).Sort key1:=.Cells(2, i), order1:=xlAscending
Next i
For i = 2 To 200 Step 1
last_row = .Cells(65536, i).End(xlUp).Row
last_row = .Cells(1, i).End(xlDown).Row
The code below will remove all null strings at the bottom of columns as well as those that contain zeroes.
Sub ClearBlankCells()
' 146
Dim Rng As Range ' working range
Dim R As Long ' intermediate: row
Dim C As Long ' loop counter: columns
Application.ScreenUpdating = False
With ActiveSheet
With .Range("B2:GQ244")
.Formula = "=IF(ISERROR(FIND( B$1,Sheet9!$H34)),"""",Sheet9!$I34)"
' replace formulas with their values
.Copy
.PasteSpecial Paste:=xlPasteValues
End With
Application.CutCopyMode = False
For C = 2 To 200 Step 1
Set Rng = .Columns(C)
R = Application.Evaluate("SUMPRODUCT((" & Rng.Address & "<>"""")*(" & _
Rng.Address & "<>0)*1)")
If R > 0 Then
Set Rng = Range(.Cells(R + 1, C), .Cells(Rows.Count, C))
Rng.ClearContents
End If
' sort by column
' Range(.Cells(2, C), .Cells(245, C)).Sort Key1:=.Cells(2, C), Order1:=xlAscending
Next C
End With
Application.ScreenUpdating = True
End Sub
Note that no blanks or zeroes may be included in the block of data above the bottom of each column, including the caption.
Sorting must be done after such cells have been removed but I left the sort instructions dimmed out because it's wrong either in syntax or by concept. If you need to sort each column the syntax is wrong because the syntax sorts the entire sheet. On the other hand, if you want to sort the entire sheet you don't have to do it in a loop 200 times.
The code runs very slowly which gives rise to two observations.
It spends 99% of its time repairing the damage it has done in its first line.
It looks at a data range which is vastly bigger than what is actually, reasonably, required. Nobody wants to look at a sheet 200 columns and 244 rows.
Therefore there must be much better ways to do achieve what you want.
I can't confirm your findings. Having a blank ActiveSheet and a blank Sheet9 the code below filled the ActiveSheet with zeroes B2:GQ244. It then read the last row xlUp as 244 and xlDown as 2. Both of these values are as expected. Perhaps you have a setting that suppresses the display of zeroes. However, as explained in my comment above, a cell that appears blank isn't necessarily blank and that would also apply to a cell containing a NullString inserted by your formula, even if the formula was subsequently removed leaving the null string in its place.
Sub Examine()
Dim last_row As Long
Dim i As Long
With ActiveSheet
.Range("B2:GQ244").Formula = "=IF(ISERROR(FIND( B$1,Sheet9!$H34)),"""",Sheet9!$I34)"
'paste by value to get rid of formulae
.Range("B2:GQ244").Copy
.Range("B2").PasteSpecial Paste:=xlPasteValues
'sort by column
For i = 2 To 200 Step 1
Range(.Cells(2, i), .Cells(245, i)).Sort Key1:=.Cells(2, i), Order1:=xlAscending
last_row = .Cells(.Rows.Count, i).End(xlUp).Row
Debug.Print last_row ' returns 244
last_row = .Cells(1, i).End(xlDown).Row
Debug.Print last_row ' returns 2
Next i
End With
End Sub
The only mystery remaining, therefore, is why .Cells(1, i).End(xlDown).Row gives you a value of 244. It doesn't. Therefore the solution must be in the conduct of your test, not in its result. Compare your testing method with the one I employed above.
situation is following:
I have 32 columns with data (various number of rows in columns) and need to delete cells with .value "downloaded" (always last cell in a column).
I have a code looping from column 32 to 1 and searching last_row for "downloaded" value. For 30 columns code seems to be working flawlessly but 2 columns return last_row value 1 even though there are multiple values (in fact hundreds of them) but they are non existent for VBA code.
Code:
Last_Col = ws.Cells(1, ws.Columns.Count).End(xlToLeft).Column
last_row = ws.Cells(Rows.Count & Last_Col).End(xlUp).Row
For R = Last_Col To 1 Step -1
With ws
Last_Col = R
last_row = ws.Cells(.Rows.Count & Last_Col).End(xlUp).Row
If Cells(last_row, Last_Col).Value Like "*Downloaded*" Then
Cells(last_row, Last_Col).ClearContents
End If
End With
Next R
Data is being drained from another worksheets. For 2 columns where I experience an error, I manually deleted values and inserted another, random batch of values and code worked as intended.
Checked columns formatting, worksheets from which data is taken but I struggle to find a solution.
Thank you for your help.
Clear Last Cell If Criteria Is Met
The main mistake was using Cells(.Rows.Count & Last_Col), where .Rows.Count & Last_Col would have resulted in a 8 or 9-digit string, while it should have been ws.Cells(ws.Rows.Count, Last_Col).End(xlUp).Row which was pointed out by chris neilsen in the comments.
Another important issue is using ws. in front of .cells, .rows, .columns, .range, aka qualifying objects. If you don't do it and e.g. the wrong worksheet is active, you may get unexpected results.
There is no need for looping backwards unless you are deleting.
Although it allows wild characters (*, ?), the Like operator is case-sensitive (a<>A) unless you use Option Compare Text.
The first solution, using the End property, will fail if a number of last columns is hidden or if you insert a new first row e.g. for a title.
The second solution, using the Find method (and the first solution), may fail if the data is filtered.
The Code
Option Explicit
Sub clearLastEnd()
Dim ws As Worksheet
Set ws = ThisWorkbook.Worksheets("Sheet1")
Dim LastCol As Long
LastCol = ws.Cells(1, ws.Columns.Count).End(xlToLeft).Column
Dim LastRow As Long
Dim c As Long
For c = 1 To LastCol
LastRow = ws.Cells(ws.Rows.Count, c).End(xlUp).Row
With ws.Cells(LastRow, c)
If InStr(1, .Value, "Downloaded", vbTextCompare) > 0 Then
.ClearContents
End If
End With
Next c
End Sub
Sub clearLastFind()
Dim ws As Worksheet
Set ws = ThisWorkbook.Worksheets("Sheet1")
Dim cel As Range
Set cel = ws.Cells.Find(What:="*", _
LookIn:=xlFormulas, _
SearchOrder:=xlByColumns, _
SearchDirection:=xlPrevious)
If Not cel Is Nothing Then
Dim c As Long
For c = 1 To cel.Column
Set cel = Nothing
Set cel = ws.Columns(c).Find(What:="*", _
SearchDirection:=xlPrevious)
If Not cel Is Nothing Then
If InStr(1, cel.Value, "Downloaded", vbTextCompare) > 0 Then
cel.ClearContents
Else
' The current last non-empty cell does not contain criteria.
End If
Else
' Column is empty.
End If
Next c
Else
' Worksheet is empty.
End If
End Sub
EDIT:
So you are curious why it worked at all. The following should shed a light on it:
Sub test()
Dim i As Long
Debug.Print "Right", "Wrong", "Rows.Count & i"
For i = 1 To 32
Debug.Print Cells(Rows.Count, i).Address, _
Cells(Rows.Count & i).Address, Rows.Count & i
Next i
End Sub
In a nutshell, Cells can have 1 or 2 arguments. When 1 argument is used, it refers to the n-th cell of a range, and it 'counts' by row. The more common usage is with 2 arguments: rows, columns. For example:
Cells(5, 10) ' refers to cell `J5`.
Using one argument is inconvenient here:
Cells(16384 * (5-1) + 10)
i.e.
Cells(65546)
It may be convenient when processing a one-column or a one-row range.
Well , let me see if i understand you have a table in worksheet table have 32 columns and X rows (because you only put WS and i can know if is WS=worksheet or WS= Table-range)
for this i am going to say is selection (if you put worksheet only hace to change for it)
in your code put:
Last_Col = ws.Cells(1, ws.Columns.Count).End(xlToLeft).Column
but in this you always wil obtein 1st cell so i dont understand why exist?
WS.columns.count
return number of columns you selection have
.End(xlToLeft)
return last cell if star to move to left (like Ctrl - left key)
so
Last_Col ---first go to cells (1,ws.Columns.Count) then go to left (End(xlToLeft)) and the end return number of column where finish (.Column) in this case you always get cell (1,"first column of your table")
NOTE: because you wrote that you have allways value in your cells (I have 32 columns with data (various number of rows in columns)
And for Row you have same question
Then you Wrote you want "Delete" but in your code you put Erase value (.ClearContents) so what do you want? because both are no equal
BUT if you have a table and want to search in any cells that have "Download" and only want to "clear content" you just may to use ".find" instead; or if you want to do all at same time you can use .replace (need to check before if .find return "nothing" or no , because if return nothing you get error)
If you have a table with 32 columns and each row have one cell where you put "Donloaded" and want to "delete" all row your code only need select column where appear "downloaded" (example Column "status").
If you have a table where any cell can take value "downloaded" and want to "delete" that cell you need to take care to resize your table and "move to" (when you delete cells you need to say where you want to move yor data remain "letf, "rigth", "up", down).
However if you say that "Downloaded" always appear in last row you can use For to change for all columns and use .end(xlDown)
For i=1 to 32
if cells(1,i).end(xlDown).value="downloaded" then cells(1,i).end(xlDown).ClearContents
next
BUT you need put more information because if you cant garantize that all cells have values and exist cells with "nothing" you will need
I am trying to extend a sum formula to a dynamic range. The sum includes all values from row 5 till the second last used row in the column, the sum is then in the last used row. However, the number of rows and columns vary, the only thing that stays the same is that the sum should always start in row 5. The sum starts also always in column C. I already managed to write a macro that puts the sum in the last row of column C. I am now working on a macro that extends this sum to all other used columns in the sheet, except for the last column (that contains another value and not a sum).
I am working with selection.autofill. However, I am having issues with declaring the range that i want to fill. VBA gives me an "expected: end of statement" error and I can't figure out why. Can somebody explain to me what I am doing wrong? Is there a better method than selection.autofill - i fear that it might not take over the columns, e.g. actually summing up column D when extended to the cell in column D ?
Here is what i already have:
'
' sumtest Macro
'
'
Dim Cell As Range, sRange As Range
Dim wsDestination As Worksheet
With ThisWorkbook
Set wsDestination = .Worksheets("Overview")
End With
FirstRow = Rows(5).EntireRow
lastRow = wsDestination.Cells(Rows.Count, "A").End(xlUp).Row
LastRow1 = wsDestination.Cells(Rows.Count, "A").End(xlUp).Row
LastColumn1 = wsDestination.Cells(4, wsDestination.Columns.Count).End(xlToLeft).Column
Worksheets("Overview").Select
wsDestination.Cells(lastRow, "C").Select
ActiveCell.Formula = "=SUM(C5:C" & lastRow - 1 & ")"
Range(wsDestination.Cells(LastRow1, 3)).Select
Selection.AutoFill Destination:=Range(wsDestination.Cells(LastRow1,3),wsDestination.Cells(LastRow1,LastColumn -1)) Type:=xlFillDefault
End Sub
This code would write the value of the sum in each column for the last row:
Option Explicit
Sub Sumtest()
With ThisWorkbook.Sheets("Overview")
Dim LastCol As Long: LastCol = .Cells(4, .Columns.Count).End(xlToLeft).Column
Dim Cell As Range
Dim LastRow As Long
For Each Cell In .Range("C4", .Cells(4, LastCol))
LastRow = .Cells(.Rows.Count, Cell.Column).End(xlUp).Row
.Cells(LastRow, Cell.Column) = Application.Sum(.Range(Cell.Offset(1), .Cells(LastRow - 1, Cell.Column)))
Next Cell
End With
End Sub
Note that the code will check the LastRow for every column, so if the columns have different amount of rows the total will be on the first row without data for each column.