For loop to change a specific cell in a formula - vba

I have a formula that shows which rows in a specific column meet a set of criteria. When the formula is executed and applied to all rows, I run a loop to check which rows returned a value as a text, and then copy-pastes this cells to another worksheet:
Sub loop1()
Dim r As Range, c As Range
With Worksheets("Sheet1")
Set r = Range(.Range("AF2"), .Range("AF2").End(xlDown))
For Each c In r
If WorksheetFunction.IsText(c) Then
Range(.Cells(c.Row, "AF"), .Cells(c.Row, "AF")).Copy
Else
GoTo nextc
End If
With Worksheets("Sheet2")
.Cells(Rows.Count, "A").End(xlUp).Offset(1, 0).PasteSpecial Paste:=xlPasteValues
End With
nextc:
Next c
End With
Application.CutCopyMode = False
End Sub
What I want to do now is to run the formula for 631 different names, copy-paste every name as a headline and then run loop1. I cant figure out though how to make the for loop work inside the formula.
Sub loop2()
Dim i As Integer
For i = 2 To 632
Sheets("Sheet1").Select
Range("AC2").Select
ActiveCell.FormulaR1C1 = _
"=IF(RC[-3]=""district1"",(IF(RC[2]=R2C33 ,(IF(RC[-18]>=1,0,(IF(RC[-16]>=1,0,IF(RC[-14]>=1,0,IF(RC[-12]>=1,0,IF(RC[-10]>=1,1,IF(RC[-8]>=1,1,IF(RC[-6]>=1,1,0))))))))),0)),0)"
Range("AC2").Select
Selection.AutoFill Destination:=Range("AC2:AC20753")
Range("AC2:AC20753").Select
Range("AG2").Select
Selection.Copy
Sheets("Sheet2").Select
ActiveSheet.Paste
Selection.Font.Bold = True
Sheets("Sheet1").Select
Application.Run "'Customers.xlsb'!loop1"
Next i
End Sub
The cells that need to be changed for every loop are, R2C33 to something like RiC33 (which doesn't work) and the "headline" Range("AG2").Select to something like Range("AGi").Select.
Anyone who could help?

The following code will do the trick:
Sub loop2()
Dim i As Integer
For i = 2 To 632
Sheets("Sheet1").Range("AC2:AC20753").FormulaR1C1 = _
"=IF(RC[-3]=""district1"",(IF(RC[2]=R" & i & "C33 ,(IF(RC[-18]>=1,0,(IF(RC[-16]>=1,0,IF(RC[-14]>=1,0,IF(RC[-12]>=1,0,IF(RC[-10]>=1,1,IF(RC[-8]>=1,1,IF(RC[-6]>=1,1,0))))))))),0)),0)"
Sheets("Sheet1").Range("AG" & i).Copy Destination:=Sheets("Sheet2").Range("A1")
Sheets("Sheet2").Range("A1").Font.Bold = True
Application.Run "'Customers.xlsb'!loop1"
Next i
End Sub
In order to let i be used within your String formula you have to stop the String " use & i & and continue the String ".
I have also changed your code to prevent the use of .Select, which is a no no in VBA.
This way it fills in your Formula copy's and changes the Font without selecting anything or changing sheets.
As Jeep noted you do however need to change Sheets(""Sheet2").Range("A1") as I don't know which cell you want to paste into.

Your first sub procedure might be better like this.
Sub loop1()
Dim r As Range, c As Range
With Worksheets("Sheet1")
Set r = Range(.Range("AF2"), .Range("AF2").End(xlDown))
For Each c In r
If WorksheetFunction.IsText(c) Then
Worksheets("Sheet2").Cells(Rows.Count, "A").End(xlUp).Offset(1, 0) = _
.Cells(c.Row, "AF").Value2
End If
Next c
End With
End Sub
Direct value transfer is preferred over a Copy, Paste Special, Values.
In the second sub procedure, you don't have to do anything but remove the 2 from R2C33; e.g. RC33. In xlR1C1 formula construction a lone R simply means the row that the formula is on and you are starting at row 2. You can also put all of the formulas in at once. Once they are in you can looop through the G2:G632 cells.
Sub loop2()
Dim i As Integer
With Sheets("Sheet1")
.Range("AC2:AC20753").FormulaR1C1 = _
"=IF(OR(AND(RC[-3]=""district1"", RC[2]=R2C33, RC[-18]>=1), SUM(RC[-16], RC[-14], RC[-12])>=1), 0, IF(SUM(RC[-10], RC[-8], RC[-6])>=1, 1, 0))"
For i = 2 To 632
.Range("AG" & i).Copy _
Destination:=Sheets("Sheet2").Somewhere
Sheets("Sheet2").Somewhere.Font.Bold = True
Application.Run "'Customers.xlsb'!loop1"
Next i
Next i
End Sub
I also tightened up your formula by grouping some of the conditions that would result in zero together with OR and AND functions.
The only thing remaining would be defining the Destination:=Sheets("Sheet2").Somewhere I left hanging.

Related

Count selected rows after auto filter

When my data are raw and unfiltered I can select them and Selection.Rows.Count returns the valid number.
After the AutoFilter it returns a number as if I selected the rows that were not visible, even though Selection.Copy does not copy other than selected rows.
How do I get the valid count of selected rows?
I tried Selection.SpecialCells(xlCellTypeVisible).Rows.Count.
EDIT
I use filter in another macro and then select by hand rows I want to add to another sheet.
I did two buttons, one to filter my table and the second to move selected rows to another sheet.
Sub ajout_commande()
Set DataSheet = ThisWorkbook.Worksheets("Prepa Commandes")
Dim a As Range, b As Range
Set a = Selection
i = 0
s = Selection.SpecialCells(xlCellTypeVisible).Count
For Each b In a.Rows
i = i + 1
DataSheet.Cells(6, 1).EntireRow.Insert
DataSheet.Range("A1:Z1").Copy DataSheet.Cells(6, 1).EntireRow
Next
Dim r1 As Range, r2 As Range, r3 As Range
Let copyrange1 = "E1" & ":" & "I" & i
Let copyrange2 = "BK1" & ":" & "BM" & i
Set r1 = a.Range(copyrange1)
Set r2 = a.Range(copyrange2)
Set r3 = Union(r1, r2)
r3.Copy
DataSheet.Cells(6, 1).PasteSpecial xlPasteValues
MsgBox s & " and " & i
End Sub
Here my table is filtered and I want to add selected rows to another sheet but the Selection.Rows.Count returns more rows than I selected because it counts the non visible rows, even though Selection.copy works.
For this example Selection.Rows.Count = 28 because of non visible rows between rows 10 and 20, 21 and 25 etc.
Is there a function to get the number I want (on this image 16)?
It depends on how you are using it. This works just fine for me
'~~> Remove any filters
ActiveSheet.AutoFilterMode = False
'~~> Specifying the complete address is the key part
With Range("A1:C6") '<~~ Filter, offset(to exclude headers)
.AutoFilter Field:=YOURFIELDNUMBER, Criteria1:=YOURCRITERIA
Debug.Print .Offset(1, 0).SpecialCells(xlCellTypeVisible).Rows.Count
End With
'~~> Remove any filters
ActiveSheet.AutoFilterMode = False
Test
Sub Sample()
'~~> Remove any filters
ActiveSheet.AutoFilterMode = False
With Range("A1:C6") '<~~ Filter, offset(to exclude headers)
.AutoFilter Field:=1, Criteria1:="Sid"
MsgBox .Offset(1, 0).SpecialCells(xlCellTypeVisible).Rows.Count
End With
'~~> Remove any filters
ActiveSheet.AutoFilterMode = False
End Sub
Well, the following would work if your selection was contiguous:
Selection.Columns(1).SpecialCells(xlCellTypeVisible).Count
However, from your screenshot I can see that your selections may be non-contiguous ranges (aka multiple areas selected), so you can use this function I created as a starting point:
Function countVisibleSelectedRows()
Dim count As Integer
count = 0
For Each Area In Selection.Areas
count = count + Area.Columns(1).SpecialCells(xlCellTypeVisible).count
Next
countVisibleSelectedRows = count
End Function
When you have multiple ranges selected, Excel calls each of those ranges an "area". In this function, we loop over each "area" in the Selection.Areas collection.
I know this is a late post to this question, but maybe this will help someone in the future. I find the following code snippet works well to count the number of visible rows in a range after being filtered.
Sub CountVisibleRows()
'only count the visible rows in the range
Dim lRow As Long, vis_lr As Long, DstWs As Worksheet
Set DstWs = ActiveSheet
lRow = DstWs.UsedRange.Rows.Count
'vis_lr = DstWs.Range("B2:B" & lRow).SpecialCells(xlCellTypeVisible).Count 'doesn't seem to work with non-contiguous rows
With DstWs
vis_lr = Application.WorksheetFunction.Subtotal(3, Range("B2:B" & lRow))
End With
Debug.Print vis_lr
End Sub

First blank ("") cell in column with IF formula

I have a macro that exactly copies one sheet's data into another.
Sub QuickViewRegMgmt()
("Reg Management").Select
Cells.Select
Selection.Copy
Sheets("Quick View Reg Mgmt").Select
Cells.Select
ActiveSheet.Paste
End Sub
I would like for this macro to also go to the last non-blank cell in Column C (or first blank, I really don't care either way). I tried simple end/offset code, e.g.
Range("A1").End(xldown).Offset(1,0).Select
My problem, however, is that the direct copy macro also copies the underlying formulas, which for Column C is an IF formula. Therefore, no cell in the column is actually empty, but rather they all have an IF formula resulting in a true/false value (respectively, a "" or VLOOKUP).
=IF(VLOOKUP('Reg Management'!$Y260,'Reg Guidance'!$A:$V,3,FALSE)=0,"",VLOOKUP('Reg Management'!$Y260,'Reg Guidance'!$A:$V,3,FALSE))
That means the end/offset code goes to the last cell in the column with the formula (C1000) instead of going to the first cell that has a value of "" (which is currently C260).
What code can I add to this macro to select the first cell that contains an IF formula resulting in a value of "" ---- which has the appearance of being blank?
After trying to be fancy with SpecialCells(), or using Find() or something I couldn't get it ...so here's a rather "dirty" way to do it:
Sub test()
Dim lastRow As Long, lastFormulaRow As Long
lastRow = Range("A" & Rows.Count).End(xlUp).Row
Dim i As Long
For i = lastRow To 1 Step -1
If Cells(i, 1).Formula <> "" And Cells(i, 1).Value = "" Then
lastFormulaRow = i
Exit For
End If
Next i
End Sub
Edit2: Here's one using .SpecialCells(). Granted I think we can whittle this down more, I like it better:
Sub lastRow()
Dim tempLastRow As Long
tempLastRow = Range("C" & Rows.Count).End(xlUp).Row
Dim lastRow As Range
Set lastRow = Columns(3).SpecialCells(xlCellTypeFormulas).Find(What:="", LookIn:=xlValues, LookAt:=xlWhole, searchdirection:=xlPrevious, after:=Range("C" & tempLastRow))
Debug.Print lastRow.Row
End Sub
It returns 10 as the row.
Edit: Be sure to add the sheet references before Range() and Cells() to get the last row. Otherwise, it's going to look at your active sheet to get the info.

Select first visible cell directly beneath the header of a filtered column

I am trying to select the first visible cell directly beneath the header of a filtered column. The code I am getting is as below, but I have to problems with this code. First, the first line of code is using the current active range of the file. It is highly likely that this file will change and this range will not be the same. How can I make it work for any file I would use it on? Second, if I use a totally different file with the same column format, the first visible cell under Column J could be J210. How can I make this work for any array of variables?
Sub Macro16()
'
' Macro16 Macro
'
'
ActiveSheet.Range("$A$1:$R$58418").AutoFilter Field:=12, Criteria1:= _
"Sheets"
Range("J2").Select
ActiveCell.FormulaR1C1 = "=RIGHT(RC[1],3)"
Selection.FillDown
End Sub
Sub FirstVisibleCell()
With Worksheets("You Sheet Name").AutoFilter.Range
Range("A" & .Offset(1, 0).SpecialCells(xlCellTypeVisible)(1).Row).Select
End With
End Sub
Untested but:
Sub Macro16()
With ActiveSheet.Range("A1").CurrentRegion
.AutoFilter field:=12, Criteria1:="Sheets"
If .Columns(1).SpecialCells(xlCellTypeVisible).count > 1 Then
With .Columns(10)
.Resize(.rows.count - 1).offset(1).SpecialCells(xlCellTypeVisible).FormulaR1C1 = "=RIGHT(RC[1],3)"
End With
End If
End With
End Sub
I prefer non-destructive methods of determining whether there are visible cells to work with after a filtering operation. Since you are filling in column J with a formula, there is no guarantee that column J contains any values tat can be counted with the worksheet's SUBTOTAL function (SUBTOTAL does not count rows hidden by a filter) but the formula you are planning to populate into column J references column K so there must be something there.
Sub Macro16()
With ActiveSheet
If .AutoFilterMode Then .AutoFilterMode = False
With .Cells(1, 1).CurrentRegion
.Columns(12).AutoFilter Field:=1, Criteria1:="Sheets"
With .Resize(.Rows.Count - 1, 1).Offset(1, 9)
If CBool(Application.Subtotal(103, .Offset(0, 1))) Then
.SpecialCells(xlCellTypeVisible).FormulaR1C1 = "=RIGHT(RC[1],3)"
End If
End With
.Columns(12).AutoFilter Field:=1
End With
End With
End Sub
      
Something like this might work...
Sub Macro16()
Dim ARow As Long, JRow As Long, ws1 As Worksheet
ws1 = Sheets("NAME OF SHEET WITH DATA")
ARow = ws1.Range("A" & ws1.Rows.Count).End(xlUp).Row + 1
ws1.Range("$A$1:$R$" & ARow).AutoFilter Field:=12, Criteria1:="Sheets"
JRow = ws1.Range("J" & ws1.Rows.Count).End(xlUp).Row + 1
ws1.Range("J" & JRow).FormulaR1C1 = "=RIGHT(RC[1],3)"
ws1.Range("J" & JRow).FillDown
End Sub

VBA Look through List

I've got the following code which gets the word dividend from a column and then takes the whole row and copy pastes it to a new sheet.
Sub SortActions()
Dim i&, k&, s$, v, r As Range, ws As Worksheet
Set r = [index(a:a,match("###start",a:a,),):index(a:a,match("###end",a:a,),)].Offset(, 6)
k = r.Row - 1
v = r
For i = 1 To UBound(v)
If LCase$(v(i, 1)) = "dividend" Then
s = s & ", " & i + k & ":" & i + k
End If
Next
s = Mid$(s, 3)
If Len(s) Then
Set ws = ActiveSheet
With Sheets.Add(, ws)
ws.Range(s).Copy .[a1]
Rows("1:1").Select
Selection.Insert Shift:=xlDown, CopyOrigin:=xlFormatFromLeftOrAbove
Sheets("20140701_corporate_action_servi").Select
Rows("2:2").Select
Selection.Copy2
Range("C32").Select
Sheets("Sheet11").Select
ActiveSheet.Paste
End With
End If
End Sub
Is there a way to make this dynamic. So if I want to search for more than word. For example if I have several rows with dividends and special dividends -> it would take all rows of dividends and all rows of special dividends and put them in separate sheets. I have tried ti with recording a macro it doesn't work as the words can differ. Maybe getting the content into a list would work. Please assist . Thanks
As suggested by #Macro Man , I am submitting images of an example sheet and sheet after filter with a simple macro for filtering one field. Please all credit to #Macro Man, it is for illustration in a simple way.
Simple code as follows.
Sub Filter1Field()
With Sheet1
.AutoFilterMode = False
With .Range("A1:H13")
.AutoFilter
.AutoFilter Field:=5, Criteria1:="Dividend"
End With
End With
End Sub
*****UPDATE*******
If your other criteria such as "Sp. Dividend" is other field but on the same row as shown in the image appended and you wish to copy to other sheet you can use the code given below. Another image shows results obtained on sheet2. You can adopt the code to your requrements.
You can use this code:
Sub Test2()
Dim LastRow As Long
Sheets("Sheet2").UsedRange.Offset(0).ClearContents
With Worksheets("Sheet1")
.Range("A1:H13").AutoFilter
.Range("A1:H13").AutoFilter field:=5, Criteria1:="Dividend"
.Range("A1:H13").AutoFilter field:=6, Criteria1:="=Sp. Dividend"
LastRow = .Range("A" & .Rows.Count).End(xlUp).Row
.Range("A1:A" & LastRow).SpecialCells(xlCellTypeVisible).EntireRow.Copy _
Destination:=Sheets("Sheet2").Range("A1")
End With
End Sub

need to apply find method within the formula for all rows

I am using vlookup in my formula in the macros . But it is taking too much time if it is applied for sheets containing more than 90k rows .
Instead i like to use find or Instr method to reduce the time . But i am getting error if i use "find: method . I dont how to use "Instr"
Sub testt()
Dim l As Long
l = Sheets(1).Range("A1:A" & Sheets(1).Cells(Sheets(1).Rows.Count, "A").End(xlUp).Row).Count
With Sheets("Sheet1")
.Range("d1").Formula = "=IF(iferror(vlookup(c1,$D:$D,1,false),"""")="""","""",1)"
.Range("d1").AutoFill Destination:=Range("d1:d" & l), Type:=xlFillDefault
End With
End Sub
here is my formula with vlookup . But i need to replace vlookup with find
l = Sheets(1).Range("A1:A" & Sheets(1).Cells(Sheets(1).Rows.Count, "A").End(xlUp).Row).Count
With Sheets("Sheet1")
.Range("d1").Formula = "=IF(iferror(range("A1:A" & l).find(c1),"""")="""","""",1)"
.Range("d1").AutoFill Destination:=Range("d1:d" & l), Type:=xlFillDefault
End With
There is no FIND function in Excel that I know of. However, you could write a VBA function to find each relevant cell in the lookup column. Here is the relevant bit of code:
Function IfFound(SearchText As String) As String
Dim FoundCell As Range
'look in particular column for a value
Set FoundCell = Columns("A:A").Find(What:=SearchText, lookat:=xlWhole)
'if found, return value in cell to its right
If FoundCell Is Nothing Then
IfFound = ""
Else
IfFound = FoundCell.Offset(0, 1).Value
End If
End Function
Here's how you could call this:
Sub TestFunction()
'make sure word TEST is in column A somewhere
MsgBox IfFound("test")
End Sub
Not sure if this is what you're looking for, but it would run much more quickly than VLOOKUP. The only problem is that you'd have to find a way to run it automatically whenever anything changed in the worksheet.