Selecting columns that have values in Excel Macro (range object in VBA) - vba

How do I modify this line in VBA to only select the columns that have values?
Set rng = Range("A1", Range("A65536").End(xlUp)).SpecialCells(xlCellTypeVisible)
I don't think I'm doing something right since the CountLarge property is several billion cells
Here is a sample of my data

#SiddharthRout Yes I only need the rows that have data. I think I have it working now with End(xlToLeft) from #JMax ... Now that I'm iterating over the cells, I can just quit the For each loop once the last row is reached. I might have this working now. – makerofthings7 14 mins ago
For this neither you need .SpecialCells nor do you need to loop through the rows :)
Here is a sample code. This will copy all the rows which have data to Sheet2 (TRIED AND TESTED)
Sub Sample()
Dim ws As Worksheet
Dim rng As Range
Dim LastRow As Long, LastCol As Long
Set ws = Sheets("Sheet1")
With ws
LastRow = .Cells.Find(What:="*", After:=.Range("A1"), Lookat:=xlPart, _
LookIn:=xlFormulas, SearchOrder:=xlByRows, SearchDirection:=xlPrevious, MatchCase:=False).Row
LastCol = .Cells.Find(What:="*", After:=.Range("A1"), Lookat:=xlPart, _
LookIn:=xlFormulas, SearchOrder:=xlByColumns, SearchDirection:=xlPrevious, _
MatchCase:=False).Column
With .Range("A1:" & Split(Cells(, LastCol).Address, "$")(1) & LastRow)
.AutoFilter Field:=1, Criteria1:="<>"
Set rng = ws.AutoFilter.Range
rng.Offset(1, 0).Resize(rng.Rows.Count - 1).Copy _
Destination:=Sheets("Sheet2").Range("A1")
End With
End With
End Sub
SNAPSHOT
I am assuming that all cells in a particular row will have data and there won't be a case like this
#makerofthings7: I think I know what exactly you are trying to do :) you don't need to use loops to achieve what you want. Just a quick question. Is it possible that say Cell C10 might have a value but B10 might not? – Siddharth Rout 12 mins ago
If there is then we will have to set the autofilter criteria accordingly.
Edit:
WAY 2
The other way would be to sort your data, pushing the blanks way down and then copying the resulting range :)
HTH

Related

Excel VBA Calculate last row for each worksheet storing the value with a different name

I need to calculate the last row in every worksheet of my excel workbook and I'm actually doing that with the following lines of code:
With Sheets("Sheetname")
If Application.WorksheetFunction.CountA(.Cells) <> 0 Then
LastRow = .Cells.Find(What:="*", _
After:=.Range("A1"), _
Lookat:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Row
Else
LastRow = 1
End If
End With
Is it possible to loop over every worksheet and store the LastRow values with a different name every time? I need all of them to be stored separately in my Macro.
You can loop through Worksheets like this:
Dim ws As Worksheet
For Each ws In ThisWorkbook.Worksheets
'do something
Next ws
You could use a Scripting.Dictionary to store your values with ws.Name as Key and your .Cells.Find as Value. Place the code where the 'do something comment is.

Auto Populate Formula to end of series in excel

I am having an issue with a piece of VBA in excel and am looking for assistance.
I require a piece of code to auto populate a formula in excel through a series of data, the series will vary in length and will occupy columns C:I.
I have been using this piece of code without issue for quiet a while:
Sub Auto_Fill_Formula()
Sheets("Sheet1").Select
Dim LstRow As Long
With Sheets("Sheet1")
LastRow = .Cells(.Rows.Count, 1).End(xlUp).Row
.Range("A2:A" & LastRow).Formula = "Formula added here"
Application.CutCopyMode = False
End With
End Sub
However as the formula is being added to the leftmost column it only populates the first cell, cell A2.
How can I modify this code to work when the leftmost column is empty?
Thank you,
Wayne
You can use Find to find the last used row in C:I by searching backwards from row 1. You should also use Option Explicit to pick up typos in variable names (LstRow).
Sub Auto_Fill_Formula()
Sheets("Sheet1").Select
Dim LastRow As Long, r As Range
With Sheets("Sheet1")
Set r = .Range("C:I").Find(What:="*", After:=.Range("C1"), Lookat:=xlWhole, SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, MatchCase:=False, SearchFormat:=False)
If Not r Is Nothing Then
LastRow = r.Row
.Range("A2:A" & LastRow).Formula = "Formula added here"
End If
End With
End Sub

Deleting empty rows generates 1004 error

I want to delete rows, if cells in a certain column are empty.
I'm using the sample code I found here.
Initially, the cells in question were in column B. I used the following:
Sub delrowEmptyStr()
Dim EmpCol As Range, LstRw As Long
LstRw = Columns(2).Find(What:="*", SearchOrder:=xlRows, SearchDirection:=xlPrevious, LookIn:=xlFormulas).Row
Set EmpCol = Cells.Find(What:="*", SearchOrder:=xlColumns, SearchDirection:=xlPrevious, LookIn:=xlFormulas).Offset(, 1).EntireColumn
With Intersect(Rows("2:" & LstRw), EmpCol)
.FormulaR1C1 = "=IF(RC2="""",""X"","""")"
.Value = .Value
.SpecialCells(xlConstants).EntireRow.Delete
End With
End Sub
I reformatted and put that data in column A. I changed the Sub to the following:
Sub delrowEmptyStr()
Dim EmpCol As Range, LstRw As Long
LstRw = Columns(1).Find(What:="*", SearchOrder:=xlRows, SearchDirection:=xlPrevious, LookIn:=xlFormulas).Row
Set EmpCol = Cells.Find(What:="*", SearchOrder:=xlColumns, SearchDirection:=xlPrevious, LookIn:=xlFormulas).Offset(, 1).EntireColumn
With Intersect(Rows("2:" & LstRw), EmpCol)
.FormulaR1C1 = "=IF(RC1="""",""X"","""")"
.Value = .Value
.SpecialCells(xlConstants).EntireRow.Delete
End With
End Sub
Running this now yields 'Run-time error 1004: No cells were found'.
Hitting 'Debug' highlights the row
.SpecialCells(xlConstants).EntireRow.Delete
I did sort my data differently before running the 2nd iteration of the Sub - I'm wondering if maybe having all the empty cells I want to get rid of to the bottom of the column may be the issue?
I'm wondering if maybe having all the empty cells I want to get rid of to the bottom of the column may be the issue?
Yes, definitely, because the statement
LstRw = Columns(1).Find(What:="*", SearchOrder:=xlRows, _
SearchDirection:=xlPrevious, LookIn:=xlFormulas).Row
yields the LstRw as the last non-empty row in that specific column. You can fix this by simply finding the last non-empty row in your whole sheet instead of the last non-empty row in the specific column:
' vvvvvv
LstRw = Cells.Find(What:="*", SearchOrder:=xlRows, _
SearchDirection:=xlPrevious, LookIn:=xlFormulas).Row

Autofiltering from a different sheet

I am trying to create a VBA macro for post processing data, and it currently has a "Raw Data" sheet for the first sheet, and my post processing tools on the second sheet. What I have so far is a button that will search the data and create plots for the desired variables, but it pulls data for all of the test points. What I want to do is to be able to filter by test point from the data review sheet. What would be ideal would be to have an autofilter type dropdown menu on my post processing sheet where the test point can be selected, and the data on the previous sheet would be filtered.
Here's the search function I've been using:
Dim TestPt As Long
Dim rows As Long
rows = Sheets(1).UsedRange.rows.Count
'
Sheets(1).Select
Cells.Find(What:="TargetTestPointNumber", After:=ActiveCell, LookIn:=xlFormulas, LookAt:= _
xlPart, SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:=False _
, SearchFormat:=False).Activate
TestPt = ActiveCell.Column
Any help would be appreciated!
I'm not 100% sure what you needed exactly. But this code does a comparison between the value you want and the data sheet. If its = it will copy it to Row D so if you have stuff there you will need to change it. Also it assumes the data is in row 1.
Dim i As Long, lastRowD As Long, lastRowA As Long
With Sheets("datasheetname")
lastRowA = .Range("A" & .Rows.count).End(xlUp).Row
For i = 1 To lastRowA
lastRowD = .Range("D" & .Rows.count).End(xlUp).Row
If .Cells(i, 1).Value = "testvalue" Then
.Cells(lastRowD, 4).Value = "testvalue"
End If
Next i
.Range("D1", "D" & lastRowD).Select
ActiveSheet.Shapes.AddChart.Select
ActiveChart.ChartType = xlXYScatter
ActiveChart.SetSourceData Source:=Range("'datasheetname'!$D$1:$D" & lastRowD)
End With
note youll need to change test value and datasheetnames. As well as any of the column info. I tried it and it works. I just have no idea if this is what you need. If you need to copy more than 1 row, you would change in the if how many to do.
.cells(lastRowD,5).value= .cells(i,2).value etc
Sorry for the messy code, but I found this to work for me. Basically I copied the unique test point value to the other sheet, linked them to a ComboBox, and linked a macro to run with the ComboBox to autofilter the data on the other sheet. I'm sure there has to be a better way, but it works for me.
Sub ValueSelectionData()
'
Dim TestPt As Long
Dim rows As Long
Dim Value As Long 'used to select test point
rows = Sheets(1).UsedRange.rows.Count 'Row count on data sheet
Value = Sheets(2).Cells(2, 6).Value 'value linked to ComboBox selection
'
Sheets(2).Columns("A:A").ClearContents
Sheets(1).Select
Cells.Find(What:="TargetTestPointNumber", After:=ActiveCell, LookIn:=xlFormulas, LookAt:= _
xlPart, SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:=False _
, SearchFormat:=False).Activate 'searches for test point column in data
TestPt = ActiveCell.Column
Range(Sheets(1).Cells(2, TestPt), Sheets(1).Cells(rows, TestPt)).AdvancedFilter Action:=xlFilterCopy, CopyToRange:=Range(Sheets(2).Cells(1, 1), Sheets(2).Cells(1, 1)), Unique:=True
If Value > 0 Then
Sheets(1).Select
Cells.Find(What:="TargetTestPointNumber", After:=ActiveCell, LookIn:=xlFormulas, LookAt:=xlPart, SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:=False, SearchFormat:=False).Activate
TestPt = ActiveCell.Column
Value = Sheets(2).Cells(2, 6).Value 'desired test point to filter for
Sheets(1).Range(Sheets(1).Cells(1, TestPt), Sheets(1).Cells(rows, TestPt)).AutoFilter Field:=1, Criteria1:=Value 'autofilters data for desired test point
Else
'Clear all auto filters
If Sheets(1).AutoFilterMode Then
Sheets(1).ShowAllData
End If
End If
End Sub

VBA - Problems dynamically selecting a range

I am new to VBA and am trying to write a macro that takes a workbook with several sheets of data and formats it for printing. Each sheet has tables of information (like operating/income statements). I want this code to be able to work for workbooks anyone creates but has the same basic information. This means I need to find start and end of the data on each sheet because it is not always in the same place (someone might start from "A1" and someone else from "B4", etc).
I've looked on the many websites for different ways to locate the first row used and last column used. What I have so far sometimes locates the starting row, ending row, starting column, and ending column correctly and other times it doesn't.
Sub FormatWorkbook()
Dim ws As Worksheet
Dim rowStart As Long
Dim columnStart As Long
Dim rowEnd As Long
Dim columnEnd As Long
Dim printStart As String
Dim printEnd As String
Application.ScreenUpdating = False
'Turn off print communication
Application.PrintCommunication = False
'Loop through sheets
For Each ws In Worksheets
'Make current sheet activesheet
ws.Select
'Set rowStart, columnStart, rowEnd, and columnEnd to the used range
With ActiveSheet
rowStart = .Cells.Find(what:="*", after:=.Range("A1"), LookAt:=xlPart, LookIn:=xlFormulas, searchorder:=xlByRows, searchdirection:=xlNext, MatchCase:=False).Row
columnStart = .Cells.Find(what:="*", after:=.Range("A1"), LookAt:=xlPart, LookIn:=xlFormulas, searchorder:=xlByColumns, searchdirection:=xlNext, MatchCase:=False).Column
rowEnd = .Cells.Find(what:="*", after:=.Range("A1"), LookAt:=xlPart, LookIn:=xlFormulas, searchorder:=xlByRows, searchdirection:=xlPrevious, MatchCase:=False).Row
columnEnd = .Cells.Find(what:="*", after:=.Range("A1"), LookAt:=xlPart, LookIn:=xlValues, searchorder:=xlByColumns, searchdirection:=xlPrevious, MatchCase:=False).Column
End With
This is just a portion of the program but the one I am most confused about. If anyone could help out I would really appreciate it. Also, if there is a better way to accomplish this task I'm all ears. The rest of my code is below for reference.
'Insert or Delete Space above the first used row
If rowStart < 4 Then
Do While rowStart < 4
Range("1:1").Select
Selection.Insert Shift:=xlDown, CopyOrigin:=xlFormatFromLeftOrAbove
ws.Select
With ActiveSheet
rowStart = .Cells.Find(what:="*", after:=.Range("A1"), LookIn:=xlValues, searchorder:=xlByRows, searchdirection:=xlNext).Row
End With
Loop
ElseIf rowStart > 4 Then
Do While rowStart > 4
Range("1:1").Select
Selection.Delete Shift:=xlUp
ws.Select
With ActiveSheet
rowStart = .Cells.Find(what:="*", after:=.Range("A1"), LookIn:=xlValues, searchorder:=xlByRows, searchdirection:=xlNext).Row
End With
Loop
End If
'I think I need to adjust the columnStart, rowEnd, and columnEnd values after inserting and deleting rows
ws.Select
printStart = ActiveSheet.Cells(1, columnStart).Address
printEnd = ActiveSheet.Cells(rowEnd, columnEnd).Address
'Format headers, footers, and set the print area
ActiveSheet.PageSetup.CenterHeaderPicture.Filename = _
"\\antilles\MyDocs\xxxx\My Documents\My Pictures\xxxx.png"
With ActiveSheet.PageSetup
.CenterHeader = "&G"
.LeftFooter = "&""Palatino Linotype,Regular""&F"
.CenterFooter = "&""Palatino Linotype,Regular""Prepared By: xxxx"
.RightFooter = "&""Palatino Linotype,Regular""&D"
.PrintArea = printStart & ":" & printEnd
End With
Next ws
Application.PrintCommunication = True
End Sub
I've done a lot of this and I recommend simple crude methods. Without coding a whole thing, but rather hinting...
dim iRow as integer
For iRow = 1 to ...
cells(iRow,65000).end(xlup).select
if activecell.row = 1 then
activecell.entirecolumn.delete
exit For
endif
next iRow
As much as possible force some structure on the sheets. Then go see
http://www.ozgrid.com/VBA/ExcelRanges.htm
When googling, include
ozgrid|Pearson|Tushar|"Mr. Excel"|Erlandsen|Peltier|dailydoseofexcel
in the search string.
Excel VBA offers a selection of techniques for finding the first or last used row or column but none work in every situation.
Some problems with your code:
If the worksheet contains something, Find returns a range. A range has a row so your statements will work. But if the worksheet is empty, Find returns Nothing. You must use Set Rng = .Cells.Find ... then test Rng to not be Nothing before accessing Rng.Row or Rng.Column. See the code I reference below if this is not clear.
Note the after in After:=.Range("A1"). Find does not examine .Range("A1") until it has searched every other cell and wraps back to the beginning. In any example in which the start point is .Range("A1") the search direction will be xlPrevious so it immediately wraps and starts searching from the bottom right cell. Try After:=.Cells(Rows.Count, Columns.Count) when the search direction is xlNext.
There was an earlier question that was similar to yours. I posted some code which showed a selection of techniques for finding the last row or column and the situations in which they fail. I suggest you visit that answer and try the code: https://stackoverflow.com/a/18220846/973283.
Good luck and happy VBA programming.