In a scenario where the user selects a whole column in Excel and I must act on cells inside the column, how do I efficiently ignore rows that are empty. A whole column has over 1 million cells! Please help!
The range comes from
var range = Application.ActiveWindow.RangeSelection;
Ultimately I want to do something using
for (int i = 0; i < range.Rows.Count; i++)
where Rows.Count should be the non-empty Row count... maybe there is a way of locating the last cell with something in it?
Several options depending on whether you have blanks inside your range (use method 1), or more simply if you want to find the last used cell (use method 3)
These options using column A of the activesheet as an example
1. SpecialCells
If the empty cells are truly empty then you can use SpecialCells to work with the formulae cells (that start with =) and/or constant cells
Sub GetNonEmtpy()
Dim rng1 As Range
Dim rng2 As Range
On Error Resume Next
Set rng1 = Columns("A").SpecialCells(xlConstants)
Set rng2 = Columns("A").SpecialCells(xlFormulas)
On Error GoTo 0
If Not rng1 Is Nothing Then MsgBox "Constants in " & rng1.Address(0, 0)
If Not rng2 Is Nothing Then MsgBox "formula in " & rng2.Address(0, 0)
'then work with these ranges
End Sub
2. Last cell look up
Sub LastCellLookup()
Dim rng1 As Range
Set rng1 = Cells(Rows.Count, "A").End(xlUp)
If rng1.Row <> 1 Then
MsgBox "last cell is " & rng1.Address(0, 0)
Else
'check first cell is not empty
If Len(rng1.Value) > 0 Then
MsgBox "last cell is " & rng1.Address(0, 0)
Else
MsgBox "row is blank"
End If
End If
End Sub
3. Find
Sub LastCellFind()
Dim rng1 As Range
Set rng1 = Columns("A").Find("*", [a1], xlValues, , xlByRows, xlPrevious)
If Not rng1 Is Nothing Then MsgBox "Last cell is " & rng1.Address(0, 0)
End Sub
It sounds to me like you want a good idea on what the upper bound on the number of rows is so you don't end up looping all the way to the bottom of the workbook.
If that's the case, then you may be looking for Worksheet.UsedRange property, which contains the range of all cells that have ever been used (this includes cells where a value was entered and then deleted).
For example,
Dim MaxUsedRow As Integer
MaxUsedRows = ActiveSheet.UsedRange.Row + ActiveSheet.UsedRange.Rows.Count - 1
MsgBox MaxUsedRows
will show the index of the last used row in entire workbook.
Related
I want to look through a table in a sheet. Find each cell with "Yes" in it, when one is found. Paste a Yes to A1, when another is found A2, etc...
I was trying to modify this code to search all cells instead of just Row A
Following code should give you the headstart
Sub Text_search()
Dim Myrange As Range
Set Myrange = ActiveSheet.UsedRange
For Each cell In Myrange
If InStr(1, cell.Value, "YES") > 0 Then
'do something
Else
'do something else
End If
Next
End Sub
Further to #isomericharsh's answer, if it's a table you're looking through, that simplifies defining the range; just use DataBodyRange.
If the table 'Table1' is on 'Sheet1' and the results are to be posted on 'Sheet2' then I'd do as follows:
Sub Search_for_Yes()
Dim YesAmt As Long ' - Amount of yes's found
YesAmt = 0 'to start with
Dim ws1 As Worksheet
Set ws1 = Sheets("Sheet1")
Dim ws2 As Worksheet
Set ws2 = Sheets("Sheet2")
'It's always safer to use specific references rather than ActiveSheet
For Each cell In ws1.ListObjects("Table1").DataBodyRange 'The data in the table excluding headings and totals
If cell.Value = "YES" Then 'might need to add wildcards to this if you want to include cells that contain yes as part of larger text string. Also note that it's case-specific.
ws2.Cells(1 + YesAmt, 1).Value = "Yes" 'so that each time a yes is found it will log it further down
YesAmt = YesAmt + 1
End If
Next
x = MsgBox(YesAmt & " values found and listed", vbOKOnly + vbInformation)
End Sub
Does that help?
I am trying to get the row number of a cell in a range that I specify with vba. I want to get the row number of the cell in the range but what I am getting is the row number of the cell in the worksheet.
This is the vba code I am using currently. I have set a range and within the range I search for a cell that contains the text string "C". Once I have found the cell I want to make changes to the value in the second column of the range.
Sub trial2()
Dim ActiveWB As Workbook
Dim ActiveWS As Worksheet
Dim rng As Range, findCell As Range
Set ActiveWB = ActiveWorkbook
Set ActiveWS = ActiveWB.ActiveSheet
Set rng = ActiveWS.Range("B4", "C10")
With rng
Set findCell = .Cells.Find(what:="C")
End With
rng.Cells(findCell.Row, 2).Value = "change to something."
End Sub
Before running the code:
After running the code:
the cell value that contains "C" is in the 6th row of the worksheet, but in the 3rd row of the range. I was wondering how do I get the 3rd row of the range. I am aware that I can just offset the cell by 1 column to solve this problem, but I am curious about getting row numbers of cells in respect to a defined range.
One option is to adjust based on the number of the first row of rng, using simple math.
rng.Cells(findCell.Row - rng.Row + 1, 2).Value = "change to something."
In this case 6 - 4 + 1 = 3.
You can use:
Option Explicit
Sub test()
Dim rng As Range
With ThisWorkbook.Worksheets("Sheet1")
'If your searching area for A,B,C etc is from B4 to B10 just use
Set rng = .Range("B4:B10").Cells.Find(what:="C")
'If your searching area for A,B,C etc is from B4 to C10 just use
Set rng = .Range("B4:C10").Cells.Find(what:="C")
If Not rng Is Nothing Then
'Way 1
.Cells(rng.Row, rng.Column + 1).Value = "change to something."
'Way 2
rng.Offset(0, 1).Value = "change to something."
Else
MsgBox "No ""C"" found."
End If
End With
End Sub
I have done some VBA in the past but just cannot find a solution for this one.
I am looking for a macro which searches cells C4 to Z4 (one infinite long row starting from C4) for a value (number) from cell B4 which changes weekly. If a match is found then copy&pastes the values of cells B5 to B100 (one infinite long column starting from B5) into the correct column C to Z (from C5 etc., downwards).
With correct column I mean the column where the macro finds the match between B4 and C4 to Z4. C4 to Z4 are non-identical.
I searched long and hard and the nearest I could find is this:
Macro that looks for a value in a cell & then paste a range in the column of that cell. EXCEL 2007
However it does not work for me. The solution in that thread says that the matching cell values should be in a date format. I recontructed all of this, but even with dates instead of numbers it does not work. The macro always gives the message according to the VBA line
MsgBox "Date Column for " & CStr([B2].Value) & " Not Found"
So it does not find any matches for me, even I run it with identical dates in the matching cells. (I changed of course this macro to my cell locations)
This forum is my final try :)
I have following code which does not work:
Private Sub CommandButton2_Click()
Dim ws As Worksheet
Dim rSrc As Range
Dim rDst As Range
Dim cl As Range
Dim dat As Variant
Set ws = ActiveSheet
' Get the Source range
Set rSrc = ws.Range([B5], ws.Columns(2).Cells(ws.Rows.Count, 1).End(xlUp))
dat = rSrc
' Find the Destination column and copy data
Set rDst = ws.Range([D4], ws.Rows(1).Cells(1, ws.Columns.Count).End(xlToLeft))
Set cl = rDst.Find(What:=[B4], _
After:=rDst.Cells(1, 1), _
LookIn:=xlValues, _
LookAt:=xlWhole, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext)
If cl Is Nothing Then
MsgBox "Column for " & CStr([B4].Value) & " Not Found"
Else
Set rDst = cl.Offset(1, 0).Resize(UBound(dat, 1), 1)
rDst = dat
End If
End Sub
Thank you.
Regards
Sub FindandCopy
Dim what as range
dim where as range
dim found as range
set what = range("b4") 'what we're looking for
set where = range("c4") 'start of search range
do
if where = what then
set found = where 'that's where we found it
else
set where = where.offset(0,1) 'otherwise keep looking
end if
loop until where = "" 'stop if blank
if found = "" then 'we fell off the end
msgbox what & " not found "
else
range(range("b5"),range("b5").end(xldown)).copy
found.offset(1,0).pastespecial xlpastevalues
end if
end sub
How do I find the first completely empty row on a particular worksheet looking top down?
Because the sheet includes a Totals row at the end of several empty rows, I don't think I can simply use the Range.End property and offset by 1.
To find the first blank row, loop through the actual UsedRange determined by Find using CountA to test for a blank row
If no row is found, use the next row after the end of the last cell determined by the Find
Sub Method2()
Dim rng1 As Range
Dim rng2 As Range
Dim bFound As Boolean
Set rng2 = ActiveSheet.Cells.Find("*", [a1], xlFormulas, , xlByRows, xlPrevious)
For Each rng1 In Range([a1], Cells(rng2.row, "A"))
If Application.WorksheetFunction.CountA(Rows(rng1.row)) = 0 Then
MsgBox rng1.row & " is the first blank row"
bFound = True
Exit For
End If
Next rng1
If Not bFound Then MsgBox rng2.Offset(1, 0).row & " is the first blank row"
End Sub
forgive me if I've misinterpreted the question.
Totals is always the last row and you'd therefore like to find the next row.
I assume that Totals changes the row it is located on and there are blank rows in front of it.
Why does the location of Totals change?
I'd just make cell where totals is located a named range "Totals":
Then if some extra rows are inserted I can still get to the Totals row ok:
So in VBA its then trivial:
Sub Method2()
MsgBox "Total named range is " & ActiveSheet.Range("Totals").Address(0, 0)
MsgBox "Next empty row is " & ActiveSheet.Range("Totals").Row + 1
End Sub
I am writing a macro that opens a New Workbook and copies all data to a different Workbook.
The data always starts on C12 but we dont always know how many rows of data there are
The following code is giving an error:
Workbooks("Somesheet.xls").Activate
Sheets("Sheet1").Activate
With Range("C12").Select
End (xlDown)
End With
How do I select all rows from C12?
dim rng as range
with Workbooks("Somesheet.xls").Sheets("Sheet1").range("C12")
set rng = range(.cells(0,0), .end(xldown))
end with
You could also use
set rng = Workbooks("Somesheet.xls").range("C12").CurrentRegion
I use Find to detect the true last used cell as UsedRange can be unreliable with over- estimating the extent of the true used range unless it is forced to recalc in the code before being used. UsedRange can also be problematic unless it start from A1 (I had this issue with Rachel's code when testing data only in C12:C40, the answer provided was G34:G60)
From your question sample code it appears you only wanted column C data from C12 down (which is what this code does). It can readily be extended accross the true used range of columns, or as entire rows if needed
Sub GetData()
Dim wb As Workbook
Dim ws As Worksheet
Dim rng1 As Range
Dim rng2 As Range
Set wb = Workbooks("SomeSheet.xlsm")
Set ws = wb.Sheets("Sheet1")
Set rng1 = ws.Columns("C").Find("*", ws.[c1], xlFormulas, , , xlPrevious)
If rng1.Row > 12 Then
Set rng2 = ws.Range(ws.[c12], rng1)
MsgBox "Your range is " & rng2.Address(0, 0)
Else
MsgBox "Last row in " & ws.Name & " was only " & rng1.Row
End If
End Sub
In order to select all data from cell C12 down, use the UsedRange property to find the absolute last row used. Other methods will stop before the true end of the sheet if there is a blank cell in Column C. This snippet sets a Range variable that spans from C12 to the last used cell in the sheet:
Dim rng As Range
Dim lRowLast As Long, lColLast As Long
With ActiveWorkbook.Worksheets("Sheet1")
lRowLast = .UsedRange.Row + .UsedRange.Rows.Count - 1
lColLast = .UsedRange.Column + .UsedRange.Columns.Count - 1
Set rng = .Range(.Range("C12"), .Cells(lRowLast, lColLast))
End With
Note: use .Row + .Rows.Count - 1 to handle cases where the used range starts after the first row.
Edit: Updated example to fix the bug that #brettdj pointed out.