VBA: Referring to active cells' row in a For/Each loop - vba

the aim of my problem is to find a specific value (Text) and then refer to the entire row (or even better only the used range to the right of my active cell) in a For/Each loop.
The first part works fine of finding my value, however, the code for targeting the row of the active cell (so the cell found by the find function), does not work yet:
Sub Search()
Dim cell As Range
Dim Count As Long
Set cell = Cells.Find(what:="Planned Supply at BP|SL (EA)", LookIn:=xlValues, lookat:=xlWhole)
For Each cell In ActiveCell.EntireRow
If cell.Value = "0" Then
Count = Count + 1
End If
Next cell
Range("I1").Value = Count
End Sub

The following code will find the range to the right of your found cell and use your loop to do the comparision for each cell in the range. That part could probably be improved by using WorksheetFunction.CountIf.
Option Explicit
Sub Search()
Dim wks As Worksheet
Set wks = ActiveSheet
Dim cell As Range, sngCell As Range
Dim Count As Long
Set cell = wks.Cells.Find(what:="Planned Supply at BP|SL (EA)", LookIn:=xlValues, lookat:=xlWhole)
If cell Is Nothing Then Exit Sub ' just stop in case no hit
Dim rg As Range, lastColumn As Long
With wks
lastColumn = .Cells(cell.Row, .Columns.Count).End(xlToLeft).Column ' last used column in cell.row
Set rg = Range(cell, .Cells(cell.Row, lastColumn)) ' used rg right from found cell inlcuding found cell
End With
' loop from the original post
For Each sngCell In rg
If sngCell.Value = "0" Then
Count = Count + 1
End If
Next
Range("I1").Value = Count
End Sub

Related

Paste to another cell and WS while looping and shifting down a row

I am looking for some help in getting this code to run properly. I've gotten some help with the first part from some great people here!
Basically, the code I have now sets ranges in between cells formatted bold, as the bold represents a date. I am trying to find the individual segments in column A and copy the coresponding number in column D to another worksheet in column C. If the value is not found in the range, the row output should shift down one without filling in anything.
Here is what I have so far:
Public Sub DataBetween()
Dim thisWB As Workbook
Dim dataWS As Worksheet
Set thisWB = ThisWorkbook
Set dataWS = thisWB.sheets("FC01.RPT")
Set MoBWS = thisWB.sheets("Mix of Business")
'--- find the first bold cell...
Dim nextBoldCell As range
Set nextBoldCell = FindNextBoldInColumn(dataWS.range("A1"))
'--- now note the start of the data and find the next bold cell
Dim startOfDataRow As Long
Dim endOfDataRow As Long
Dim lastRowOfAllData As Long
startOfDataRow = 3
'Set lastRowOfAllData = dataWS.Cells(ws.Rows.Count, "A").End(xlUp).Row
'--- this loop is for all the data sets...
Do
endOfDataRow = EndRowOfDataSet(dataWS, startOfDataRow)
'--- this loop is to work through one data set
For i = startOfDataRow To endOfDataRow
sheets("FC01.RPT").Select
Cells.Find(What:="Individual return guest").Activate
range("D" & (ActiveCell.Row)).Select
Selection.copy
sheets("Plan").Select
range("C3").Select
ActiveSheet.Paste
Next i
startOfDataRow = endOfDataRow + 1
Loop
'Do While endOfDataRow < lastRowOfAllData
errhandler:
MsgBox "No Cells containing specified text found"
End Sub
Public Function FindNextBoldInColumn(ByRef startCell As range, _
Optional columnNumber As Long = 1) As range
'--- beginning at the startCell row, this function check each
' lower row in the same column and stops when it encounters
' a BOLD font setting
Dim checkCell As range
Set checkCell = startCell
Do While Not checkCell.Font.bold
Set checkCell = checkCell.Offset(1, 0)
If checkCell.Row = checkCell.Parent.Rows.Count Then
'--- we've reached the end of the column, so
' return nothing
Set FindNextBoldInColumn = Nothing
Exit Function
End If
Loop
Set FindNextBoldInColumn = checkCell
End Function
Private Function EndRowOfDataSet(ByRef ws As Worksheet, _
ByVal startRow As Long, _
Optional maxRowsInDataSet As Long = 50) As Long
'--- checks each row below the starting row for either a BOLD cell
' or, if no BOLD cells are detected, returns the last row of data
Dim checkCell As range
Set checkCell = ws.Cells(startRow, 1) 'assumes column "A"
Dim i As Long
For i = startRow To maxRowsInDataSet
If ws.Cells(startRow, 1).Font.bold Then
EndRowOfDataSet = i - 1
Exit Function
End If
Next i
'--- if we make it here, we haven't found a BOLD cell, so
' find the last row of data
EndRowOfDataSet = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
End Function
The code keeps crashing. How can I make it so the output line shifts down one when looping though a range, no matter if it finds the value or not?
Does anyone know what do to?
Here is a snapshot of the data I am working with:
Thanks for the help!!
I noticed all your "blocks" end with some "Summe" occurrence in column A, and data begins at row 14
then I'd go this way:
Sub mm()
Dim iArea As Long
With Worksheets("FC01.RPT")
With .Range("A14", .Cells(.Rows.Count, 1).End(xlUp))
.Cells(2, 1).Value = "Summe"
.AutoFilter field:=1, Criteria1:="Summe*"
With .Resize(.Rows.Count - 1).Offset(1).SpecialCells(xlCellTypeVisible) '.Offset(-1)
For iArea = 1 To .Areas.Count - 1
With .Parent.Range(.Areas(iArea).Offset(1), .Areas(iArea + 1).Offset(-1))
Worksheets("Plan").Cells(Rows.Count, "D").End(xlUp).Offset(1).Value = WorksheetFunction.SumIf(.Cells, "Individual*", .Offset(, 3))
End With
Next
End With
.Cells(2, 1).ClearContents
End With
.AutoFilterMode = False
End With
End Sub

How to avoid using select in VBA for variable cell ranges?

I have heard of the dislike for using .select in VBA for excel macros, but I am wondering how my particular goal can be achieved without its use? For example, say there is a cell(used as a header) with the value "Commodity". Beneath it, all cells need to have a VLookup function. However, on each and every iteration of the macro, the column will shift (as new columns are added) and new rows will be added (so that newly added rows will need to have the function added as well). How is it possible to consistently locate this Commodity column and find its lowest unfilled row? It is very simple to do using select:
Do Until ActiveCell.Value = "Commodity"
Activecell.offset(0,1).select
loop
Do Until ActiveCell.Value = ""
ActiveCell.offset(1,0).select
loop
Obviously, I would prefer to avoid using this type of syntax, but I do not know how to get around it. All answers I have seen regarding the avoidance of select appear to set, for example, rng = Cell(x,y) or something, but they are always known-location cells. I do not know how to do this without utilizing select to check cell values.
First find the column that your Sting is located, then count the rows beside it, set your range and enter the formula.
Sub FindColumn()
Dim f As Range, c As Integer
Dim LstRw As Long, rng As Range
Set f = Rows(1).Find(what:="Commodity", lookat:=xlWhole)
If Not f Is Nothing Then
c = f.Column
Else: MsgBox "Not Found"
Exit sub
End If
LstRw = Cells(Rows.Count, c - 1).End(xlUp).Row
Set rng = Range(Cells(2, c), Cells(LstRw, c))
rng = "My Formula"
End Sub
Here are two iterate rows to based on the ActiveCell.
Sub Examples()
Dim Target As Range
Dim x As Long
Set Target = ActiveCell
Do Until Target.Value = "Commodity"
Set Target = Target.Offset(0, 1)
Loop
Do Until ActiveCell.Offset(x, 1).Value = ""
x = x + 1
Loop
End Sub
Assuming the wanted header IS there, you can use this function:
Function FindLowestUnfilledCell(headerRow As Range, header As String) As Range
With headerRow.Find(What:=header, lookat:=xlWhole, LookIn:=xlValues, MatchCase:=False) '<--| look for header in passed row
Set FindLowestUnfilledCell = headerRow.Parent.Cells(headerRow.Parent.Rows.Count, .Column).End(xlUp)
End With
End Function
to be used by your main sub as follows
Sub main()
FindLowestUnfilledCell(Rows(1), "Commodity").Formula = "myformula"
End Sub
should the absence of the wanted header be handled, the same function gets a little longer like follows
Function FindLowestUnfilledCell(headerRow As Range, header As String) As Range
Dim r As Range
Set r = headerRow.Find(What:=header, lookat:=xlWhole, LookIn:=xlValues, MatchCase:=False) '<--| look for "Commodity" in row 1
If Not r Is Nothing Then Set FindLowestUnfilledCell = headerRow.Parent.Cells(headerRow.Parent.Rows.Count, r.Column).End(xlUp)
End Function
and its exploitation would consequently take into account the possibility of not founding the wanted header:
Sub main()
Dim lowestUnfilledRange As Range
Set lowestUnfilledRange = FindLowestUnfilledCell(Rows(1), "Commodity")
If Not lowestUnfilledRange Is Nothing Then lowestUnfilledRange.Formula = "myformula"
End Sub
I want to simplify the answer a bit. For example
Set r = ActiveCell
MsgBox r.Address ' $A$1
Columns("A").Insert ' insert column before the first column
MsgBox r.Address ' $B$1
so you can change your code to
Dim cell As Range ' optional
Set cell = ActiveCell
While cell = "Commodity"
Set cell = cell(, 2) ' similar to Set cell = cell.Resize(1,1).Offset(, 1)
Wend
While cell = ""
Set cell = cell(, 2)
Wend

How to debug VBA code using .Find and Offset?

I'm practising VBA and I need some help / correction for my code.
In this task I'm creating a search tool which looks up each worksheet for the selected value from a combobox. Each result is listed on the first page.
Problems:
In the code I defined the .Find method in to a range rFound. On each worksheet the searched value is at column D. I would like to copy the row from column B to E. I've commented an attempt how did I tried to select that range, with offset but I receive an error. Why and how to fix that?
When I want to paste (list) the results I want it to start from the 1st page 3rd row column K. After running the code it selects the right target but pastes nothing. How to fix this?
I've also made some attempts to copy the document header after each search result, but I commented them out, please ignore lines with getOwner.
Dim ws As Worksheet, OutputWs As Worksheet, wsLists As Worksheet
Dim rFound As Range ', getOwner As Range
Dim strName As String
Dim count As Long, LastRow As Long
Dim IsValueFound As Boolean
'Dim cboSelectName As ComboBox
Dim a As String
IsValueFound = False
Set OutputWs = Worksheets("Teszt") '---->change the sheet name as required
LastRow = OutputWs.Cells(Rows.count, "A").End(xlUp).Row
Set wsLists = Worksheets("Lists")
a = ComboBox1.Value
On Error Resume Next
strName = a
If strName = "" Then Exit Sub
For Each ws In Worksheets
If ws.Name <> "Output" Then
With ws.UsedRange
Set rFound = .Find(What:=strName, After:=.Cells(1, 1), LookIn:=xlValues, LookAt:=xlWhole)
If Not rFound Is Nothing Then
Application.Goto rFound, True
IsValueFound = True
'MsgBox rFound.Row
rFound.EntireRow.Copy
'Rfound keres - rFound.Range(rFound(Offset(-2,")),rFound.Offset(1,"")).Copy ' ---> This is a suggestion
OutputWs.Cells(LastRow + 2, 11).PasteSpecial xlPasteAll
'getOwner.Range(K2, R2).Copy ' attempt to copy the header for each search result
'getOwner.Cells(LastRow + 1, 6).Paste
Application.CutCopyMode = False
LastRow = LastRow + 1
End If
End With
End If
Next ws
On Error GoTo 0
If IsValueFound Then
OutputWs.Select
MsgBox "Search Complete!"
Else
MsgBox "Value not found"
End If
You are selection entire row but you are pasting it to the column K. If you copy entire row, you can only copy it to column A. That's why it is not working. So I suggest you to work on Offset part.
In Offset, first part is rows, second part is columns.
you can do something like that,
Dim sth as Range
set sth = .range(.rfound.offset(0,-2),.rfound.offset(0,1)).copy
But I am not sure of it. Not very good at that.

Check merged cell and compare adjacent to set unique value from compared cells values

I'm writing a macro in Excel 2010 for a problem that is as follows:
I have two columns, one with a Key string value and one with a uuid. The idea is that every key should have only one uuid but as the table is now, key cell could be merged cells or single cells.
The macro needs to recognize which cells are merged and which are not, so, I have two options:
If cell is merged, check all its adjacent cells, pick first uuid value and copy/paste it to other adjacent cells, that is to say, cell below(Could be with an Offset())
If cell is not merged , but key value is repeated in multiple cells, copy/paste uuid value to adjacent cells.
So basically is to check merged cells MergeArea but I don't know if I need to iterate through its addresses or check cells in the range with an offset of Offset(0,1) or what.
With my code I can know if the cells are merged but now, how con I iterate through it's adjacent cells values?
Code as is now:
Sub CopyUUID()
Dim lRow As Long
Dim rng As Range
Dim ws As Worksheet
Dim rMerged As Range
Dim value As Variant
Set ws = Sheets(ActiveSheet.Name)
On Error GoTo ExitProgram 'If an error happens within the execution, skips it and continue in next step
Application.DisplayAlerts = False 'We can cancel the procedure without errors
With ws
lRow = .Range("F" & .Rows.count).End(xlUp).row
Set rng = .Range(.Cells(3, 6), .Cells(lRow, 6))
rng.Select
For Each cell In rng
If cell.MergeCells Then
'Code for merged cells
Else
'Code to use for single cells
End If
Next cell
End With
ExitProgram:
Exit Sub
End Sub
Option Explicit
Sub CopyUUID()
Const UUID As Long = 31 'col AE
Dim lRow As Long, cel As Range, isM As Boolean, copyID As Boolean, kCol As Long
With ActiveSheet
kCol = -25 'col F
lRow = .Cells(.Rows.Count, UUID + kCol).End(xlUp).Row
For Each cel In .Range(.Cells(3, UUID), .Cells(lRow, UUID))
isM = cel.Offset(0, kCol).MergeCells
copyID = isM And Len(cel.Offset(0, kCol)) = 0
copyID = copyID Or (Not isM And cel.Offset(0, kCol) = cel.Offset(-1, kCol))
If copyID Then cel = cel.Offset(-1)
Next
End With
End Sub
Try the following code. Note that this is going to overwrite the current contents of UUID, so make a backup copy before testing. If you don't want the UUID column modified, you can modify this to suit your needs.
Sub CopyUUID()
Dim lRow As Long
Dim rng As Range
Dim c As Range
Dim ws As Worksheet
Dim rMerged As Range
Dim value As Variant
Set ws = Sheets(ActiveSheet.Name)
On Error GoTo ExitProgram 'If an error happens within the execution, skips it and continue in next step
' Application.DisplayAlerts = False 'We can cancel the procedure without errors
With ws
lRow = .Range("F" & .Rows.Count).End(xlUp).Row
Set rng = .Range(.Cells(3, 6), .Cells(lRow, 6))
' rng.Select
For Each c In rng
If c.MergeCells Then
'Code for merged cells
c.Offset(0, 1).Formula = c.MergeArea.Cells(1, 1).Offset(0, 1).Formula
Else
'Code to use for single cells
If c.Formula = c.Offset(-1, 0).Formula Then
c.Offset(0, 1).Formula = c.Offset(-1, 1).Formula
End If
End If
Next c
End With
ExitProgram:
Exit Sub
End Sub
When in a MergedCell, it makes the UUID the same as the UUID of the first cell in the merged area. When not in a MergedCell, it copies UUID from the row above if Key is the same as the row above.
I changed your variable cell to c (I don't like to use variable names that can be confused with built-ins) and commented out a couple of lines.
Hope this helps
I adopt a simple approach to this problem as illustrated through steps taken by me.
sample sheet showing data with merged cells and unmerged cells.
Run the program code to unmerge the cells. Output of the program is appended below.
If this structure of data matches your case then addition of 2 lines of code for column B will leave the data as per following image.
Program code is as follows:
'Without column deletion:
Sub UnMergeRanges()
Dim cl As Range
Dim rMerged As Range
Dim v As Variant
For Each cl In ActiveSheet.UsedRange
If cl.MergeCells Then
Set rMerged = cl.MergeArea
v = rMerged.Cells(1, 1)
rMerged.MergeCells = False
rMerged = v
End If
Next
End Sub
'With coumn deletion
Sub UnMergeRangesB()
Dim cl As Range
Dim rMerged As Range
Dim v As Variant
For Each cl In ActiveSheet.UsedRange
If cl.MergeCells Then
Set rMerged = cl.MergeArea
v = rMerged.Cells(1, 1)
rMerged.MergeCells = False
rMerged = v
End If
Next
Columns("B:B").Select
Selection.Delete Shift:=xlToLeft
End Sub

Delete cells with a specific value

I am trying to do something simple. From column N in Log Frame info copy only unique values starting at B62 of Dropdown - this part works! Then, if one of the values in B62:B80 is "other" delete that cell - this part works sometimes and not others, can't tell why. Help!
Sub test()
Dim RngDest As Range
Dim Rng As Range, Cell As Range
Sheets("Dropdowns").Range("b61:b80").ClearContents
Set Rng = Sheets("Log Frame Info").Range("N4:N500")
Set RngDest = Sheets("Dropdowns").Range("B62")
Rng.AdvancedFilter Action:=xlFilterCopy, CopyToRange:=RngDest, Unique:=True
With Sheets("Dropdowns")
Set Rng = Range("B61:b80")
For Each Cell In Rng
If Cell = "Other" Then
Cell.Delete
End If
Next Cell
End With
End Sub
The reason is because once a cell has been deleted, the For loop is continuing to the next cell rather than evaluating the new value of the cell. Something like this should work as it counts when a cell has been deleted and offsets the If call:
Sub test()
Dim RngDest As Range
Dim Rng As Range, Cell As Range
Dim i As Integer
Sheets("Dropdowns").Range("b61:b80").ClearContents
Set Rng = Sheets("Log Frame Info").Range("N4:N500")
Set RngDest = Sheets("Dropdowns").Range("B62")
Rng.AdvancedFilter Action:=xlFilterCopy, CopyToRange:=RngDest, Unique:=True
With Sheets("Dropdowns")
Set Rng = Range("B61:b80")
For Each Cell In Rng
If Cell.Offset(-i, 0) = "Other" Then
Cell.Delete
i = i + 1
End If
Next Cell
End With
End Sub