Loop through first cells of selected merged ranges only - excel

I am trying to give the user the option to do simple arithmetic operations on selected cells.
The thing is that most cells are merged ranges.
I got the following already but the problem with it is that it loops through all cells while I only want it to only affect those cells that are not merged or only to the first cells of merged ranges.
Sub test()
Application.ScreenUpdating = False
Dim cel As Range
Dim selectedRange As Range
myValue = InputBox("Enter")
Set selectedRange = Application.Selection
For Each cel In selectedRange.Cells
On Error Resume Next
cel.Value = Evaluate(cel.Value & myValue)
Next cel
End Sub

Although VBasic2008's answer works, it's not totally correct. The problem is that each cell in merged range always returns True for MergedCells property. This means that excessive processing is done in a loop (i.e. incrementing a value) for cells other than top-left cell. To fix this situation, you should test each cell for the need to process. You can do this in several ways:
You can compare the address of a cell with the top-left cell address (Option 1 in code).
You can test the length of cell's value. If it's zero, then it's not top-left cell, so you skip it (Option 2 in code).
Code:
Sub IncrValues()
Dim rng As Range, myValue%
myValue = InputBox("Enter")
For Each cell In Selection
If cell.MergeCells Then
'// Option 1:
If cell.Address = cell.MergeArea(1).Address Then
cell.Value = cell.Value + myValue
End If
'// Option 2:
'If Len(cell) > 0 Then
' cell.Value = cell.Value + myValue
'End If
Else
cell.Value = cell.Value + myValue
End If
Next
End Sub

The MergeCells Property
Using the MergeCells property in an If statement, you check if a cell is not merged, then execute the following statement(s), otherwise the statement(s) after Else.
In the following example, the range I3:M12 is selected and 5 is entered as myValue. The first table is the state of the second table before.
The Code
Sub test()
Dim cel As Range
Dim selectedRange As Range
Dim myValue As Double
Application.ScreenUpdating = False
myValue = InputBox("Enter")
Set selectedRange = Application.Selection
For Each cel In selectedRange.Cells
If Not cel.MergeCells Then
' If not merged cell.
cel.Value = Evaluate(cel.Value & myValue)
Else
' if merged cell.
cel.Value = Evaluate(cel.Value + myValue)
End If
Next cel
Application.ScreenUpdating = True
End Sub
Count
We can expand the previous tables by adding a COUNT column,
where it is more obvious how the merged cells are being 'ignored' in Excel i.e. all cells except the first cell of a merged area will not be counted (or summed up, or ...).
The following shows the difference between counting the cells in VBA and in Excel.
Sub MergeTest()
With Range("J3:J12")
Debug.Print .Cells.Count
Debug.Print WorksheetFunction.Count(.Cells)
End With
With Range("J3:N12")
Debug.Print .Cells.Count
Debug.Print WorksheetFunction.Count(.Cells)
End With
End Sub
The results in the Immediate window are
10,
9,
50,
46,
which shows how VBA will count every cell, but Excel will exclude all cells of a merged area except the first.
In VBA Help search for the MergeArea property for some further info.

Related

Excel VBA Inserting a Row in a Loop for Each Occurrence

I'm trying to build a VBA application that checks for a certain value, then adds a row on top for each time this value is found.
Sub copy()
Dim rng As Range
Dim row As Range
Dim cell As Range
Set rng = Range("B2:B10")
For Each row In rng.Rows
For Each cell In row.Cells
If cell.value = "test" Then
MsgBox "found" + cell.Address
cell.EntireRow.Insert
End If
Next cell
Next row
End Sub
Every time I try to run this function, however, it keeps adding rows on top of each other continuously and not for each occurrence.
If you loop the cells from top to bottom, adding the row will push your original range down, causing the next loop to evaluate the previous cell.
To avoid this, loop backwards (i.e. bottom to top):
Sub copy_test()
Dim rng As Range
Set rng = Range("B2:B10")
Dim i As Long
For i = rng.Cells.Count To 1 Step -1
If rng.Cells(i).Value = "test" Then
Debug.Print "Found"
rng.Cells(i).EntireRow.Insert
End If
Next i
End Sub
Note: Set rng = Range("B2:B10") is telling VBA that you are referring to Cells B2:B10 of the ActiveSheet which might not be what you want.
Please fully qualify your range to avoid this. (e.g. ThisWorkBook.Worksheets("Sheet1").Range("B2:B10") or use the code name of the worksheet Sheet1.Range("B2:B10").)

Apply loop to check if value is equal between ranges

I am trying to highlight a cell if it doesn't equal the value defined in a worksheet range.
I am checking each cell in the range "ADS_Export[ADS_208_SZ]" against Worksheets(ADS_Validator").Range("E3:E500") but it doesn't like the range E3:E500.
Seems to work if I just put E3 but all after the first cell check are incorrect as its checking against the third row, not the next row in the worksheet range (E4 etc.).
It needs to check the first value in the ADS_Export range against the first value in the worksheet range which starts at E3 and then does the next one. Also, need to redefine to highlight if not equal instead of equal.
Only just getting into VBA so my knowledge is very limited.
Sub IF_Loop()
Dim cell As Range
For Each cell In Range("ADS_Export[ADS_208_SZ]")
If cell.Value = Worksheets("ADS_Validator").Range("E:E").Value Then
cell.Interior.Color = 65535
End If
Next cell
End Sub
Try this code, please:
Sub IF_Loop()
Dim cell As Range, celVal As Range, lastRow As Long
lastRow = Worksheets("ADS_Validator").Range("E" & Rows.Count).End(XlUp).row
For Each cell In Range("ADS_Export[ADS_208_SZ]")
For Each cellval In Worksheets("ADS_Validator").Range("E3:E" & lastRow)
If cel.value = celVal.value Then
cell.Interior.Color = 65535
End If
Next
Next cell
End Sub

How to test if a Range is blank?

I'm making a college exercise and I need to check if some specific cells are blank. If they are blank, I need to write something in them. I've tried to make one program, but it did not worked, showing me the error: 13.
Here is my code:
Option Explicit
Sub Test()
If Range("a1:e1").Value = "" Then
Range("a1:e1") = "x"
End If
End Sub
Thank you for the help!
If a range contains 1 cell then its .Value property returns a single scalar value.
However, If a range contains multiple cells then its .Value property returns an Array.
You could iterate over all the cells in the range to see if they are all empty. Alternatively, you could use WorksheetFunction.CountBlank to see if the number of blanks in the range matches the number of cells in the range.
With Range("a1:e1")
If WorksheetFunction.CountBlank(.Cells) = .Cells.Count Then
Range("a1:e1") = "x"
End If
End With
It's not clear what you want to do in case one cell in this range is not empty.
Since the case where you want to fill the cells if they are all blank has been already covered, I will cover the case where you want to check if there are any blank cells in a range and if yes then fill them.
Dim cell As Range
For Each cell In Range("A1:E1").Cells
If cell.Value = "" Then
cell.Value = "x"
End If
Next cell
You could also use WorksheetFunction.CountA. It counts non empty cells. Like this:
If WorksheetFunction.CountA(Range("a1:e1")) = 0 Then
Range("a1:e1") = "x"
End If
It does the same as TinMan's code. But the check is differend.
You could try this code:
Sub Test()
Dim rng As Range
Set rng = Range("A1:E1")
' if we come upon non-empty cell we exit sub and do nothing
For Each cell In rng
If Not IsEmpty(cell) Then Exit Sub
Next
' if we reached this, all cells are empty
rng.Value = "x"
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

How do I, using a macro, decrease all the values in a selected range by 1

I've searched for similar questions, but the problem I'm having is that I'm adding what I'm looking for to an existing recorded macro that uses commands like:
Range(Selection, Selection.End(xlUp)).Select
to select a range.
This uses the Counta function, assuming there aren't any blanks in the data. I also added IsNumeric in case the value is text. If you wanted to go a step further to make sure everything is neat, you can add this right before the If statement:
Sub Minus_One()
Dim Cell As Range
Dim Cells As Range
Set Cells = Sheets(WhateverSheet).Range("A1:A" & Application.WorksheetFunction.CountA(Range("A:A")))
For Each Cell In Cells
If IsNumeric(Cell.Value) = True Then Cell.Value = Cell.Value - 1
Next Cell
End Sub
Cell.Value = Trim(Cell.Value)
Sub test()
Dim rngTarget As Range
Set rngTarget = Sheet1.Range("A1")
Set rngTarget = Range(rngTarget, rngTarget.End(xlDown))
Dim rngIndex As Range
For Each rngIndex In rngTarget
With rngIndex
.Value = .Value - 1
End With
Next rngIndex
End Sub

Resources