Select non-blank rows in spreadsheet - excel

I am going to try and keep this as short as I can and still explain adequately, here goes :)
I have searched forums, my VBA literature, and cannot find a way to do what I'm trying.
I have a spreadsheet with rowTotal >= 60 rows. The rows either have text data in cells of each column, or the rows are blank with a pattern and colorindex set.
I need a macro to select all non-blank rows.
I first tried looping through the cells of column A (if a cell in column A has text data, then its row should be selected), checking if activecell.value <> empty.
Here's the jist (mix of pseudocode & code):
Range("A1").Select
loop to end
if activeCell.value <> empty then
stringVar = stringVar + cstr(activeCell.row) + ":" + cstr(activeCell.row) + ","
end if
end loop
stringVar = Left(stringVar, (Len(stringVar) - 1))
Range(stringVar).Select
If I have total 10 rows with rows 2 and 8 having data, stringVar would resolve to this: "2:2, 8:8".
Range(stringVar).Select would have same result as writing Range("2:2, 8:8").Select.
If the number of rows to be in the range is <= 45, this works no problem. However, as soon as the number of rows with data in them exceeds 45, the code fails on Range(stringVar).Select.
I tried the macro recorder and it gets around this by using the Union method. And so I thought, "self, you can get this done with Union(). hooray MacroRecorder." But alas, my joy was remiss.
I was thinking I could split the one large string into 1 or more strings; each of these smaller strings would be under the 45 limit mentioned above. Then I can use Union() to group all the ranges (these smaller strings) together into the one desired range.
However, I would have to "build" my Union() code in real time during code execution, after I knew how many of these 45> strings I had.
Anyone know how to take a worksheet and select just rows that contain data; which amounts to having a range of non-contiguous rows where more than a count of 45 rows are selected.

No need for loops - use SpecialCells
For column A only use:
Set rng1 = Columns("A").SpecialCells(xlCellTypeConstants).EntireRow
instead.
Sub QuickSet()
Dim rng1 As Range
On Error Resume Next
Set rng1 = Cells.SpecialCells(xlCellTypeConstants).EntireRow
On Error GoTo 0
If Not rng1 Is Nothing Then
MsgBox "Your working range is " & rng1.Address(0, 0)
Else
MsgBox "No constants found"
End If
End Sub

I first suggest you try using Autofilter. If you're using Excel 2010 (and prob 2007, but I can't check) this is as simple as selecting your data, choosing the "Data" tab, then clicking Filter. Using the drop-down box in your first column, deselect "blanks".
The exact same functionality exists in Excel 2003, under the Data/Filter menu option. I can't really remember it all that well, though; you'll have to experiment, or Google it.
If that doesn't work:
Sub it()
Dim cell As Range
Dim selectRange As Range
For Each cell In ActiveSheet.Range("A:A")
If (cell.Value <> "") Then
If selectRange Is Nothing Then
Set selectRange = cell
Else
Set selectRange = Union(cell, selectRange)
End If
End If
Next cell
selectRange.Select
' selectRange.EntireRow.Select 'If you want to select entire rows
End Sub

Just used this code and it worked a treat - been tracking all other excel forums but couldn't find anything that was as simplified.
I also added that the selected rows were copied and pasted to the next blank row in another sheet, if anyone finds this useful.
Sub copypaste1()
'Find rows that contain any value in column A and copy them
Dim cell As Range
Dim selectRange As Range
For Each cell In ActiveSheet.Range("A:A")
If (cell.Value <> "") Then
If selectRange Is Nothing Then
Set selectRange = cell
Else
Set selectRange = Union(cell, selectRange)
End If
End If
Next cell
selectRange.EntireRow.Select
selectRange.EntireRow.Copy
'Paste copied selection to the worksheet 'mega' on the next blank row
Sheets("mega").Range("A65536").End(xlUp).Offset(1, 0).PasteSpecial _
Paste:=xlPasteValues
End Sub

Related

Apply formula to all visible cells in column CK, occasionally there will be no rows

I have a filter applied to column CK, I am able to select the next visible row from the header by using the following, which also applies a formula into that active cell.
How do I fill that formula down to the bottom, without affecting the hidden rows?
Occasionally there will be no data, so it's just applying a formula to a blank row..
range("CK1").Select
ActiveSheet.range("$A$1").AutoFilter Field:=89, Criteria1:="0"
' Add if formula to find missing carriers based on patterns
Do
ActiveCell.Offset(1, 0).Select
Loop While ActiveCell.EntireRow.Hidden = True
ActiveCell.Formula2R1C1 = _
"=IFS(AND(LEN(RC[1])=18,LEFT(RC[1],2)=""1Z""), ""UPS"", AND(LEN(RC[1])=12,ISNUMBER(RC[1])),""FedEx"",AND(LEN(RC[1])=10,ISNUMBER(RC[1])),""DHL"",AND(LEN(RC[1])=11,LEFT(RC[1],2)=""06""),
It would be great if you could refrain from selecting cells or activating sheets or workbooks like you do. The only time it is fine to have Excel change its selection on screen with VBA is if you want it to.
For your problem, a simple loop will do. Example with CK1 and all the cells below it:
Dim topCell As Range, bottomCell As Range
Set topCell = Range("CK1")
Set bottomCell = topCell.end(xlDown)
'Next test is optional, although recommended (is there no cell filled under CK1?)
If bottomCell.Row >= 1048576 Then 'Current maximal row; you may change the threshold if desired.
Exit Sub
'Alternatively: Exit Function
'Other alternative example: Set bottomCell = Range("CK1000")
End If
Dim c As Range
For Each c In Range(topCell, bottomCell)
If Not c.EntireRow.Hidden Then
c.Formula2R1C1 = "" '<place your formula here>
End If
Next c

Use active cell as a reference to look up/right to find the first cell with specific back ground color

I wanted to use the value in table header and row header as an input to calculate the value in active cell. For example column header has value 10 and row header as a value 10, the output would be 10*10 = 100.
I have managed to build a macro which I specify the row and column I want to use and it will loop through my selection of cells and do the calculation.
I want to further automate the code by asking the macro to lookup from my active cell in the selection and find the cell with specific color and take the column/row reference. The column and row header has specific color coding to them.
But I am not sure how to use find with combination of ActiveCell and asking it to lookup in a specific direction.
I need it to start searching from ActiveCell due to there might be multiple table in a tab and I want the calculation to be done for my selected range.
Illustration of selection & active cell
Let's try to solve this problem in the reverse order - not from the current cell to search for the table in which it is located, but to find all the tables on the sheet and select from them the one in which the cell fell.
The main work - finding tables on the sheet - will be done by Function UsedAreas(). A few years ago I found this code here, today, when I remembered it, I found it there again. (Thanks #Ejaz_Ahmed! Not sure that you are here now, but anyway - Thanks!)
The rest of the code is very simple:
Sub getCellInfo(aCell As Range, ByRef hRow As Variant, ByRef hColumn As Variant)
Dim ws As Worksheet
Dim EachArea As Range
Dim UsedAreasRange As Range
Set ws = aCell.Worksheet
Set UsedAreasRange = UsedAreas(ws.UsedRange)
hRow = Empty
hColumn = Empty
For Each EachArea In UsedAreasRange.Areas
If Not Application.Intersect(aCell, EachArea) Is Nothing Then
hRow = ws.Cells(aCell.Row, EachArea.Column).Value2
hColumn = ws.Cells(EachArea.Row, aCell.Column).Value2
Exit Sub
End If
Next EachArea
End Sub
For testing, I used this:
Sub a_getCellInfo_test()
Dim aCell As Range
Dim hR As Variant, hC As Variant
Set aCell = ActiveCell
Call getCellInfo(aCell, hR, hC)
If IsEmpty(hR) Then
MsgBox "Cell " & aCell.Address(False, False) & " not in a table"
Else
MsgBox "Cell " & aCell.Address(False, False) & " has Row '" & hR & "' and Column '" & hC & "'"
End If
End Sub
Select any cell and run macro
PS. I have no doubt that someone will demand to quote the Function UsedAreas() code under the pretext that the external resource may close and the code will be lost. I respect copyright. Anyone who wants to break them can go to the specified link and steal the code on their own.
You can use the "For each" loop to loop through each cell in your selection.
Sub test()
For Each loopcell In Selection
'If you already select an range of cell, "Selection" means the range you selected
If loopcell.Interior.Color <> 65535 Then
'your code if the cell is target color 65535 is yellow in color as an example
End If
Next
End Sub

Find row indices of empty cells in a given range

Is it possible to write a vba macro that determines if there are any empty cells in a given range and returns the row number of that cell?
I'm new to vba and all that I managed to write after searching the internet was something that takes a range and colors every emty cell in it red:
Sub EmptyRed()
If TypeName(Selection) <> "Range" Then Exit Sub
For Each cell In Selection
If IsEmpty(cell.Value) Then cell.Interior.Color = RGB(255, 0, 0)
Next cell
End Sub
The macro does basically what I want, but instead of coloring the empty cell red I would like to know the row index of the empty cell.
A little background info: I have a very large file (about 80 000 rows) that contains many merged cells. I want to import it into R with readxl. Readxl splits merged cells, puts the value in the first split cell and NA into all others. But a completely empty cell would also be assigned NA, so I thought the best thing would be to find out which cells are empty with Excel, so that I know which NA indicate a merged cell or an empty cell. Any suggestions on how to solve this problem are very welcome, thanks!
Edit: To clarify: Ideally, I want to unmerge all cells in my document and fill each split cell with the content of the previously merged cell. But I found macros on the web that are supposed to do exactly that, but they didn't work on my file, so I thought I could just determine blank cells and then work on them in R. I usually don't work with Excel so I know very little about it, so sorry if my thought process is far too complicated.
To do exactly what you state in your title:
If IsEmpty(cell.Value) Then Debug.Print cell.Row
But there are also Excel methods to determine merged cells and act on them. So And I'm not sure exactly what you want to do with the information.
EDIT
Adding on what you say you want to do with the results, perhaps this VBA code might help:
Option Explicit
Sub EmptyRed()
Dim myMergedRange As Range, myCell As Range, myMergedCell As Range
Dim rngProcess As Range
With Application
.ScreenUpdating = False
.Calculation = xlCalculationManual
.EnableEvents = False
End With
Set rngProcess = Range("A1:B10")
For Each myCell In rngProcess
If myCell.MergeCells = True Then
Set myMergedRange = myCell.MergeArea
With myMergedRange
.MergeCells = False
.Value = myCell(1, 1)
End With
End If
Next myCell
With Application
.ScreenUpdating = True
.Calculation = xlCalculationAutomatic
.EnableEvents = True
End With
End sub
Note that I explicitly declare all variables, and I hard coded the range to check. There are various ways of declaring the range to be checked; using 'Selection' is usually rarely preferred.
Before anything else: From the opposite end of the spectrum, you can use Range.MergeCells or Range.MergeArea to determine if a Cell is part of a Merged Area. But, I digress...
You can use Cell.Row to get the row number. How you return or display that is up to you - could be a Message Box, a delimited string, or an array, or even a multi-area range.
A Sub cannot return anything once called, so you may want a Function instead, e.g. Public Function EmptyRed() As String
(Also, I would recommend you get in the habit of explicitly declaring all of your variables, and perhaps using Option Explicit too, before you run into a typo-based error. Just add Dim cell As Range at the top of the sub for now)
Sub FF()
Dim r, wksOutput As Worksheet
Dim cell As Range, rng As Range, rngArea As Range
With Selection
.UnMerge
'// Get only blank cells
Set rng = .SpecialCells(xlCellTypeBlanks)
'// Make blank cells red
rng.Interior.Color = vbRed
End With
'// Create output worksheet
Set wksOutput = Sheets.Add()
With wksOutput
For Each rngArea In rng.Areas
For Each cell In rngArea
r = r + 1
'// Write down the row of blank cell
.Cells(r, 1) = cell.Row
Next
Next
'// Remove duplicates
.Range("A:A").RemoveDuplicates Array(1), xlNo
End With
End Sub
There are a couple ways:
Sub EmptyRed()
Dim rgn,targetrgn as range
Dim ads as string ‘ return rgn address
Set targetrgn= ‘ your selection
For Each rgn In Targetrgn
If IsEmpty(rgn.Value) Then
‘1. Use address function, and from there you can stripe out the column and row
Ads=application.worksheetfunction.addres(cell,1)’ the second input control the address format, w/o $
‘2. Range.row & range.column
Ads=“row:” & rgn.row & “, col: “ & rgn.column
End if
Next rgn
End Sub
Ps: I edited the code on my phone and will debug further when I have a computer. And I am just more used to use “range” rather than “cell”.
To clarify: Ideally, I want to unmerge all cells in my document and fill each split cell with the content of the previously merged cell.
Cycle through all cells in the worksheet's UsedRange
If merged, unmerge and fill the unmerged area with the value from the formerly merged area.
If not merged but blank, collect for address output.
Sub fillMerged()
Dim r As Range, br As Range, mr As Range
For Each r In ActiveSheet.UsedRange
If r.Address <> r.MergeArea.Address Then
'merged cells - unmerge and set value to all
Set mr = r.MergeArea
r.UnMerge
mr.Value = mr.Cells(1).Value
ElseIf IsEmpty(r) Then
'unmerged blank cell
If br Is Nothing Then
Set br = r
Else
Set br = Union(br, r)
End If
End If
Next r
Debug.Print "blank cells: " & br.Address(0, 0)
End Sub

Excel VBA - Paste to visible cells

I have a template that's linking to external source.
My predecessor created it and that for 'easiness' on the eye, he/she created it by skipping a row. i.e. row 1 then row 3, row 5, row 9, row 13 etc HAS FORMULA, whereas in between those mentioned rows are just DEFAULT EMPTY CELL.
I've created a vba that opens the workbook and copy the sheet that I want.
If I were to use the code below, it's running very slowly, and for some reason, it loops more than once.
for each cell in usedrange
if cell.hasformula = true and instr(cell.formula, "SUMIF") > 0 then
cell.formulaR1C1 = "='\\tel\folder1\folder2\[xlsheet.xlsx]SheetName'!RC
end if
next cell
Therefore, what I've done is to actually assign it once, copy it and then paste to the respective cells (as shown below).
Workbooks(desWB).Sheets(maxSheet + 1).Range("J5").FormulaR1C1 = fullPath
Workbooks(desWB).Sheets(maxSheet + 1).Range("J5").Copy
Workbooks(desWB).Sheets(maxSheet + 1).Range("J6:J12,E48:J55,E57:J58,E61:J79,E84:J93,E96:J96,E99:J103").PasteSpecial Paste:=xlPasteFormulas
The latter method works and it's definitely much faster than the first. However, now I am facing a situation where due to the setup of the template, some rows have formulas and some doesn't, and it goes to thousands of rows. The skipping of rows too sometimes is not an increment of 2, it could be 3, 5 etc.
So am wondering if there's a way that's more effective and efficient to:
Look at the used range
If range has formula AND formula has 'SUMIF'
Change the formula to something else
Else SKIP and check next cell
If you only want to process rows where the first cell in that row has a non-empty cell value then you should iterate the Ranges rows and columns and skip the rows when the first cell fails the test.
Your current code that uses a For Each cell in range approach will still keep processing cells in an empty row - which is redundant.
You can use code like below to skip the blank rows and only apply conditional logic to rows where you are confident that some cells have the formula you want to update. In the example, I use Range("C4:E10") but you can substitute for the Range that works for you depending on your workbook structure.
Option Explicit
Sub Test()
'could pass in UsedRange of the sheet...
IterateRange ThisWorkbook.Worksheets("Sheet1").Range("C4:E10")
End Sub
Sub IterateRange(rng As Range)
Dim rngCell As Range
Dim intX As Integer
Dim intY As Integer
'iterate all cells in range
For intX = 1 To rng.Rows.Count
For intY = 1 To rng.Columns.Count
'get a cell
Set rngCell = rng.Cells(intX, intY)
'check if cell is blank or empty
If IsEmpty(rngCell.Value) Or rngCell.Value = "" Then
'skip the rest of the columns in this row and goto next row
Exit For
Else
'this row has non-empty cells - do something
Debug.Print rngCell.Address
'some other test
If rngCell.HasFormula And InStr(1, rngCell.Formula, "SUMIF") Then
'update formula...
Debug.Print rngCell.Formula
End If
End If
Next intY
Next intX
End Sub
Code line to execute:
Range("A1:A10").SpecialCells(xlCellTypeVisible).Value = "1"
'This line send 1 to Visible Cells in A1:A10 range

Excel vba - Search for data in 2 columns then move row to another sheet

I have to sort through several hundred rows in a spreadsheet. I would like to try and automate this task with vba code but I don’t know how to go about it.
In the spreadsheet I need to search down column ‘A’ (until the end of the data) and identify any cells in column ‘A’ that contain the text/word “asset”. If a cell does contain the text “asset” check along the same row to the cell in column ‘N’. If the corresponding cell on the same row in column ‘N’ is blank, move the entire row to a new sheet called ‘Removed’
Can anyone please help with some vba code
Thanks
Ian
Do you know how many rows exactly you need to check or do you need a function that stops automatically?
Sub CopyPaste()
Dim MyRange as Range
Dim Current_Range as Range
'Nb_Rows can be any integer, even higher than the real number of rows.
For i = 1 To Nb_Rows
If Range("A" & i).Value = "asset" Then
If Range("N" & i).Value = "" Then
Set Current_Range = Cells(i, 1)
If MyRange Is Nothing Then
Set MyRange = Current_Range
Else
Set MyRange = Application.Union(MyRange, Current_Range)
End If
End If
End If
Next i
If MyRange Is Nothing Then
Exit Sub
Else
MyRange.EntireRow.Cut
Sheets("Removed").Paste
End If
End Sub
This piece of code should work.
I used Nb_Rows for the loop, maybe you should change that value.
Be aware that I copy/paste the rows, meaning that they will stay in the first sheet. I also assume that you launch the macro when the first sheet is activated.

Resources