In Excel 2013, having sheet named "Tags", I am trying to set a printing area from A2 till end of page, ending with column L.
Worksheets("Tags").PageSetup.PrintArea = Worksheets("Tags").Range( _
Cells(2, 1), Cells(Worksheets("Tags").Range("A65536").End(xlUp).Row, 12))
My code compiles okay, but it does not seems to work - no printing area has been set.
What should be a correct macro to set printing area?
It's easier to see what is happening if you declare a few variables and decompose your statement.
Try this:
Sub SetPrintArea()
Dim ws As Worksheet
Dim lastRow As Long
Set ws = ThisWorkbook.Sheets("Tags")
' find the last row with formatting, to be included in print range
lastRow = ws.UsedRange.SpecialCells(xlCellTypeLastCell).Row
ws.PageSetup.PrintArea = ws.Range("A2:L" & lastRow).Address
End Sub
Alternatively, if you want to find the lastRow with data, you can find the lastrow like this:
lastRow = ws.Cells.Find("*", SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row
Note that the 65536 value you're using as the starting point to find the last row is obsolete (although it will frequently still work) as of Excel 2007, which has over a million rows per sheet.
A few things to note about your approach:
Cells(2,1) is A2. The syntax is Cells([row], [column])
You want the last populated row in column L, but are looking in column A instead. Range("A65536").End(xlUp).Row
This results in a print area (once you've added the .Address to your range) of A1:L2. Why A1? Because the column A is empty, and the lastrow is therefore row 1. You have set the range to be A2:L1, which becomes A1:L2.
You need to add .Address at the end of your code.
Worksheets("Tags").PageSetup.PrintArea = Worksheets("Tags").Range( _
Cells(2, 1), Cells(Worksheets("Tags").Range("A65536").End(xlUp).Row, 12)).Address
PageSetup.PrintArea Property
Returns or sets the range to be printed, as a string using A1-style references in the language of the macro. Read/write String.
Related
The task at hand is to search in column A to see what values I have (they are in form of letters) and paste for each unique entry, its value once in another column.
Here is a visual explanation:
What I came up with was to create a For loop that iritiates through column A and created a conditional that if it found a certain value then it would insert the value in the range. Here is the code:
For i = 1 to 26
if cells(i,26).value= "A" Then
Range ("C1")= "A"
Elseif cells(i,26).value = "B" then
Range ("C2").value = "B"
ElseIf (i,26).value = "C" then
Range ("C3").value = "C"
EndIf
Next i
end sub
I want to cut this process short as my data set is really big with lots of company names. Any recommendations? I believe there has to be a way of knowing the values without having to look at all the values yourself.
If the goal is to just get a unique list of values found in Column A output to Column C you can use the below macro. This is really just recreating the steps of one method you would manually take to find unique values. Not the most sophisticated solution, but it works
Create a copy of your column with company names (using last available column in sheet)
De-dup the helper column
Copy the de-duped column to destination
Delete the helper column
Assumes the last column on worksheet is not used
Sub Unique()
Dim ws As Worksheet: Set ws = ThisWorkbook.Sheets("Sheet1")
Dim lr As Long, lc As Long
'Determine Range Size
lr = ws.Range("A" & ws.Rows.Count).End(xlUp).Row
lc = ws.Cells(1, ws.Columns.Count).Column
'Copy Company Names To Helper Column/Remove Duplicates
ws.Range("A2:A" & lr).Copy ws.Cells(1, lc)
ws.Columns(lc).RemoveDuplicates Columns:=1, Header:=xlNo
lr = ws.Cells(ws.Rows.Count, lc).End(xlUp).Row
'Output Unique Values From Helper Column
ws.Range(ws.Cells(1, lc), ws.Cells(lr, lc)).Copy
ws.Range("C2").PasteSpecial xlPasteValues
'Delete Helper Column
ws.Columns(lc).Delete
End Sub
Note my comment on post. VBA may not be needed here at all
Here's a slightly different version of using .RemoveDuplicates which also removes blank cells.
You can also do this without VBA. Just copy the desired column to another and use Remove Duplicates under Data tab.
Sub Unique_Values()
Dim ws As Worksheet: Set ws = ThisWorkbook.Sheets("Sheet1")
'Getting all the values in column A (except header)
'Copying them into cell C2 and below
ws.Range("A2", Range("A1048576").End(xlUp)).Copy Range("C2")
'setting the header for the column C
ws.Range("C1").Value = "What companies are in Column A?"
'Removing duplicates and blanks from column C
With ws.Range("$C$2", Range("C1048576").End(xlUp))
.Value = .Value
.RemoveDuplicates Columns:=1, Header:=xlNo
On Error Resume Next
.SpecialCells(xlCellTypeBlanks).Delete xlShiftUp
On Error GoTo 0
End With
End Sub
Although I agree with the coding convention used in the other answer, I think it is over-complicating the problem a little bit that would cause confusion for beginners.
I think both answers so far will give you exactly what you want, and perhaps could be simplified even further?
Sub GetUniqueQuick()
Dim LastRow As Long
Application.ScreenUpdating = False
LastRow = Sheets("Sheet1").Cells(Rows.Count, 1).End(xlUp).Row
Sheets("Sheet1").Range("A2:A" & LastRow).Copy Sheets("Sheet1").Range("C2")
Sheets("Sheet1").Range("C1:C" & LastRow).RemoveDuplicates Columns:=1, Header:=xlYes
End Sub
Using the dynamic features of MS 365 you can simply apply the worksheet function UNIQUE() over a given range, e.g.
= UNIQUE(A2:A100)
or integrate it in a user defined function
Function GetCompanies(rng As Range)
If rng.Columns.Count > 1 Then Exit Function ' allow only one column
GetCompanies = Application.Unique(rng) ' return function result as 2-dim array
End Function
As empty cells result in pseudo-uniques with a 0 output, you could call them in formula with an added cosmetical blank string :
=GetCompanies(A2:A100)&""
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 completely new to VBA, so this task is a bit difficult for me but I bet it is easy for you guys.
I am trying to create a macro command that can automatically convert a series of dates from text to a date format that excel can recognize. This is a task which I regularly perform, so it would be very time saving to have a macro doing it for me.
Basically, I regularly download a time series of e.g. the historical price of a stock. The length of the time series varies every time.
Next I will need to convert the dates from the downloaded data to a format excel can recognize.
To do so I use the following code:
=DATE(RIGHT(B2,4),MONTH("1 "&MID(B2,4,3)),LEFT(B2,2))
in the cell adjacent to the first row of the date series.
I then auto-fill this formula to the end of the series.
I have created a macro that performs this task for me, using the following code:
Sub FacsetDates()
' FacsetDates Macro
' Turn Factset dates into excel format
'
' Keyboard Shortcut: Ctrl+Shift+D
ActiveCell.FormulaR1C1 = _
"=+DATE(RIGHT(RC[-1],4),MONTH(""1 ""&MID(RC[-1],4,3)),LEFT(RC[-1],2))"
Selection.End(xlToLeft).Select
Dim Lastrow As Long
Lastrow = Cells(Rows.Count - 1, ActiveCell.Column).End(xlUp).Row
Selection.End(xlToRight).Select
Selection.AutoFill Destination:=ActiveCell.Range("A1:A" & Lastrow - 1)
ActiveCell.Range("A1:A" & Lastrow - 1).Select
End Sub
My problem is that this code only works if the date series start from row 2.
If the the series is inserted from row 1 the auto-fill will stop one row short and if the series start from row 3, the auto-fill will fill out one row too much (compared to the length of the data series)
I would like a macro that works no matter which row the data series start.
E.g. I would like the macro to work even if the date series begin at B10.
I imagine that the solution is to set the data series as an array in VBA and then perform a loop that manipulate each string of text, and then finally paste the manipulated data in the adjacent column.
I have started producing the following code:
Sub FSdate()
Dim arrMarks() As Long
Lastrow = Cells(Rows.Count, ActiveCell.Column).End(xlUp).Row
ReDim arrMarks(1 To Lastrow)
Dim i As Long
For i = LBound(arrMarks) To UBound(arrMarks)
arrMarks(i) = ActiveCell
Next i
In which I try to first define the array and its size, and then "copy" the string of text from the active cell (being the first row of the data series), but this code fails.
After having defined the array, I imagined to run a loop that use the DATE function from above to manipulate every single entry in the array. But my current skills in VBA falls short here, and I simply do not know how to proceed.
Can anyone help create such a code?
or even, do you guys have inputs to alternative ways of doing this task?
Probably the initial code can be manipulated to work no matter which row the data series start.
I hope somebody is able and willing to help me!
This is a vary simplified breakdown of #Dave answer, since you want to use the cell you are selecting to start from. First; set your last row by counting the rows in the column to the left from your active cell. Second; set your range from the active cell to the last row variable. Third: write your formula into the range. Note: the lRow - ActiveCell.Row + 1 adjusts your range based on the activecell row number.
Dim lRow As Long
lRow = Cells(Rows.Count, ActiveCell.Offset(, -1).Column).End(xlUp).Row
ActiveCell.Resize(lRow - ActiveCell.Row + 1).FormulaR1C1 = "=+DATE(RIGHT(RC[-1],4),MONTH(""1 ""&MID(RC[-1],4,3)),LEFT(RC[-1],2))"
An easier way to accomplish your task; by overwriting the current text would be to use TextToColumns
ActiveSheet.Columns("F").TextToColumns Destination:=ActiveSheet.Columns("F"), DataType:=xlDelimited, _
TextQualifier:=xlDoubleQuote, FieldInfo:=Array(1, 3), TrailingMinusNumbers:=True
Columns("F").NumberFormat = "m/d/yyyy"
If we first look at what's happening:
Sub FacsetDates()
' FacsetDates Macro
' Turn Factset dates into excel format
'
' Keyboard Shortcut: Ctrl+Shift+D
' Enter Formula in the current cell
ActiveCell.FormulaR1C1 = _
"=+DATE(RIGHT(RC[-1],4),MONTH(""1 ""&MID(RC[-1],4,3)),LEFT(RC[-1],2))"
' Move to the leftmost cell in a contiguous range from the current cell
Selection.End(xlToLeft).Select
Dim Lastrow As Long
' Get the row number of the bottom cell in the same column as the now selected cell
Lastrow = Cells(Rows.Count - 1, ActiveCell.Column).End(xlUp).Row
' Move to the rightmost cell in a contiguous range from the now selected cell
Selection.End(xlToRight).Select
' Fill down from the current cell by the same number of cells in the range from A1 to the last row
Selection.AutoFill Destination:=ActiveCell.Range("A1:A" & Lastrow - 1)
ActiveCell.Range("A1:A" & Lastrow - 1).Select
End Sub
Where your issue with the range comes in is that ActiveCell.Range("A1:A" & Lastrow - 1) does not refer to rows 1 to x in the sheet, it refers to rows 1 to x in your range which starts at row 2 or 3 or whatever.
You will also learn very quickly that changing selections in code is time/resource consuming and is susceptible to bugs creeping in eg if selections change during the running of your code.
I would consider hardcoding the column where you are outputting your formula if it is always going to be the same to and to avoid making selections. You can do this and input the formula directly into column C like so:
Sub FacsetDates2()
Dim Lastrow As Long
' Get the row number of the bottom cell in column A
Lastrow = Cells(Rows.Count - 1, 1).End(xlUp).Row
Range("C2:C" & Lastrow).FormulaR1C1 = "=+DATE(RIGHT(RC[-1],4),MONTH(""1 ""&MID(RC[-1],4,3)),LEFT(RC[-1],2))"
End Sub
EDIT -
Using the active cell and going to the end of the range as defined in column A you could use this:
Sub FacsetDates2()
Dim Lastrow As Long
Dim c As Range
Dim currentRow As Long
Dim currentColumn As String
' Store a reference to the active cell
Set c = ActiveCell
' Get the row number and column name of the active cell
currentRow = c.Row
currentColumn = Replace(c.Address, currentRow, "")
' Get the row number of the bottom cell in column A
Lastrow = Cells(Rows.Count - 1, 1).End(xlUp).Row
Range(c.Address & ":" & currentColumn & Lastrow).FormulaR1C1 = "=+DATE(RIGHT(RC[-1],4),MONTH(""1 ""&MID(RC[-1],4,3)),LEFT(RC[-1],2))"
End Sub
I have the example where I want to write a VBA statement which will select all data in a single column, there are no blanks in the column data. The column position will never change e.g. column A, and the data starts in row 3. However the total number of rows in the column will change regularly.
I want the system to dynamically select all the cells in column and then I can run a method against these selected pieces of data.
As an example of performing an action on your range without selecting it:
Public Sub Test()
Dim rColA As Range
With ThisWorkbook.Worksheets("Sheet1")
Set rColA = .Range(.Cells(3, 1), .Cells(.Rows.Count, 1).End(xlUp))
MsgBox "Column A range is " & rColA.Address 'Delete if you want.
rColA.Interior.Color = RGB(255, 0, 0) 'Turn the back colour red.
rColA.Cells(2, 1).Insert Shift:=xlDown 'Insert a blank row at second cell in range
'So will insert at A4.
'If the first cell in your range is a number then double it.
If IsNumeric(rColA.Cells(1, 1)) Then
rColA.Cells(1, 1) = rColA.Cells(1, 1) * 2
End If
End With
End Sub
Try
Dim LastRow as Long, sht as worksheet
Set sht = ThisWorkbook.Worksheets("My Sheet Name")
LastRow = sht.Cells(sht.Rows.Count, 1).End(xlUp).Row
sht.Range("A3:A" & LastRow).Select
Like Darren Bartrup-Cook says, you may not need to select the data, you can almost always perform actions directly which is much faster.
If your column is "isolated" meaning no other nonblank cells touch your data you can use:
Range("firstCellInYourColumn").CurrentRegion.Select
(this works the same way as Ctrl+* from keyboard)
otherwise use:
Range(Range("firstCellInYourColumn"), Range("firstCellInYourColumn").End(xlDown)).Select
both will work if there are really no blanks within your data.
You should also prepend all Range with worksheet expression, I omitted this.
What I am trying to do should be easy but the noob in me is showing. I am creating a database in excel using a dataentry form in another worksheet. When the “enter” button is clicked, it runs a macro that places the info from the dataentry sheet to an Excel database. The problem is, of course, that the next record overwrites the previous record. What I want to do is copy the data from a field in the dataentry form, then go to the database and find the next empty row and paste the info there. Ideally, the procedure would continue and copy the next field in the dataentry form, find the empty cell to the right of the data previously pasted and then repeat until all 6 fields have been copied and pasted to the excel database. I want to do this all in excel rather than into an access database. Any ideas?
You could try
Sub LastRow()
Dim rngLast As Range
With ThisWorkbook.Worksheets("Sheet1")
If .Range("A1").Value = "" Then
Set rngLast = .Range("A1")
Else
Set rngLast = .Cells(.Rows.Count, "A").End(xlUp).Offset(1, 0)
End If
MsgBox "Next blank row is " & rngLast.Address
End With
End Sub
This will find the very last cell (last column & last row) used in the workbook & sheet provided. Adapt to taste (e.g you don't need targetwkb if you only work with one workbook). In case you don't need to find the last column, or just want the specific last row of a given column, you could just define different the "With" part (w.e. "With Worksheets(targetSheet).Range("A:A") or something similar:
Function FindLastCell(targetWbk As String, targetSheet As String) As Range
Dim LastColumn As Integer, lastRow As Integer
'Finds the last used cell in target wbk/sheet
With Workbooks(targetWbk).Worksheets(targetSheet)
lastRow = .Cells.Find(What:="*", After:=[A1], SearchOrder:=xlByRows, SearchDirection:=xlPrevious).row
LastColumn = .Cells.Find(What:="*", After:=[A1], SearchOrder:=xlByColumns, SearchDirection:=xlPrevious).Column
Set FindLastCell = Range(.Cells(1, 1), .Cells(lastRow, LastColumn))
End With
End Function
The above is a better approach overall because it searches from the last cell to the top. The otherway around, if you have an empty cell then Excel will stop searching, and you could overwrite all the data that follows just because a blank cell was left somewhere.
If you have lots of data (like 10s of thousands) you may want to change integer for long, so you don't bump on the integer limit. It also doesn't need to be a function, could be a sub but doesn't matter much...