Autofill Application.Countifs.Formula VBA Excel - vba

I think this might be a simple question but I can't seem to find a way to autofill an Application.WorksheetFunction.SumProduct(wsf.CountIf formula so that the row number would change as the formula goes down.
I want to autofill this formula for B2 till LastRow from Column A
Application.WorksheetFunction.SumProduct(wsf.CountIfs(Sheet2.Range(**B2 Here**), Doctors, Sheet2.Range(**B2 Here**), wsf.Transpose(Emergency)))
So lets assume that the last used row for column A is 6, so the formula should autofill from B2 until B6 such as:
B3 = Application.WorksheetFunction.SumProduct(wsf.CountIfs(Sheet2.Range(**B3 Here**)....
B4 = Application.WorksheetFunction.SumProduct(wsf.CountIfs(Sheet2.Range(**B4 Here**)....
B5 = Application.WorksheetFunction.SumProduct(wsf.CountIfs(Sheet2.Range(**B5 Here**)....
B6 = Application.WorksheetFunction.SumProduct(wsf.CountIfs(Sheet2.Range(**B6 Here**)....
I don't want to use .Formula "=SUMPRODUCT(COUNTIFS....." approach so that I can use arrays as the criteria vary according to the selected dropdown choices.
Any clues?

AutoFill can do this for you. Assuming your formula begins in B2 and the end row changes, the syntax would be:
LRow = Sheet1.Cells(Rows.Count,1).End(xlUp).Row
With Range("B2")
.Formula = 'Your formula here.
.AutoFill Destination:=Range("B2:B" & LRow)
End With
Another option would be using FillDown as AutoFill almost always have quirks. The syntax is pretty much the same:
LRow = Sheet1.Cells(Rows.Count,1).End(xlUp).Row
Sheet1.Range("B2").Formula = 'Your formula here.
Sheet1.Range("B2:B" & LRow).FillDown
Let us know if this helps.
UPDATE:
Much like Dave's answer, here's my take on your formula:
Sub Test()
Dim Sh As Worksheet
Set Sh = ThisWorkbook.Sheets("Sheet1") 'Modify as required.
With Sh
LRow = .Cells(Rows.Count, 1).End(xlUp.Row)
.Range("B2").Formula = "=SUMPRODUCT(COUNTIFS($C$2:$C$16, ""Doctors"", $I$2:$I$16, $B2))"
.Range("B2:B" & LRow).FillDown
End With
End Sub
HOWEVER, here's the clincher: If you are merely doing a COUNTIFS checking if Column C has Doctors as value and Column I has the value of B2, you should remove the SUMPRODUCT from your formula as it's not necessary. SUMPRODUCT was once the way to check multiple conditions, but in Excel 2007 and up, it's been completely replaced by COUNTIFS for simple multiple condition-based check-and-count. What I'm saying is, the proper code is pretty much the following:
Sub Test()
Dim Sh As Worksheet
Set Sh = ThisWorkbook.Sheets("Sheet1") 'Modify as required.
With Sh
LRow = .Cells(Rows.Count, 1).End(xlUp.Row)
.Range("B2").Formula = "=COUNTIFS($C$2:$C$16, ""Doctors"", $I$2:$I$16, $B2)"
.Range("B2:B" & LRow).FillDown
End With
End Sub
STILL, further formula forensics and evaluation reveal a trickier part: You are entering your formula in B2 and below, but your COUNTIFS is checking B2 and below as part of the condition as well. This is not only confusing but is also prone to error (if not already erroneous) as it will result into a circular reference.
At this point, my question now is, what exactly are you trying to check and/or count? This is more than just a VBA issue, in my opinion, as I believe that you are confused as to what and how you want to get your numbers. While our approach is sound and is basically perfect for your question, if the formula is incorrect, the above is useless. :)

Try this.
Here's how my data is laid out:
This code goes in regular module:
Sub Test(s As String) 'input frpm combo box
Dim Sh As Worksheet, LRow As Long
Set Sh = ThisWorkbook.Sheets("Sheet1") 'Modify as required.
If LCase(s) = "all" Then s = "*"
s = Chr(34) & s & Chr(34) 'bracket with quotes
With Sh
LRow = .Cells(Rows.Count, 1).End(xlUp).Row
.Range("B2").Formula = "=COUNTIFS($C$2:$C$16," & s & ", $I$2:$I$16, $A2)"
.Range("B2:B" & LRow).FillDown
End With
End Sub
This code goes in sheet module of combo box (Sheet1 in this instance).
Private Sub ComboBox1_Change()
Test Me.ComboBox1.Value
End Sub
I'm not sure of your formula results, but it is getting loaded correctly.

Related

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.

How to add a Formula To Cell using VBA [duplicate]

This question already has answers here:
How do I put double quotes in a string in vba?
(5 answers)
Closed 1 year ago.
I am attempting to write some VBA which will add header text to 3 cells then fill a formula all the way down to the last row. I have written the below, which writes the headers no problems, but when it get's to my first .Formula it throws a
Application Defined or Object Defined error
What needs to be altered so that this macro will execute successfully? (The formulas were pulled directly from the formula in the cell, so I know they are valid formulas at least on the "front-end")
Function Gre()
Range("E2").Select
ActiveCell.FormulaR1C1 = "Under"
Range("F2").Select
ActiveCell.FormulaR1C1 = "Over"
Range("G2").Select
ActiveCell.FormulaR1C1 = "Result"
With Range("E2:E" & Cells(Rows.Count, "C").End(xlUp).Row)
.Formula = "=IF(C2<B2,B2-C2,"")"
End With
With Range("F2:F" & Cells(Rows.Count, "C").End(xlUp).Row)
.Formula = "=IF(C2>B2,C2-B2,0)"
End With
With Range("G2:G" & Cells(Rows.Count, "C").End(xlUp).Row)
.Formula = "=IF(F2>0,'Issue',"")"
End With
End Function
The problem is likely that you are escaping the quotes with the formula.
What you need is:
.Formula = "=IF(C2>B2,B2-C2,"""")"
for the first one, for example. The other quotes need to be doubled as well.
As a side-note, it would also be best to specify the sheet you are working on with something like:
Dim ws as worksheet
Set ws = Sheets("mySheet")
ws.Range("E2").FormulaR1C1 = "Under"
etc.
If you don't do this, you can sometimes have errors happen while running the code.
As suggested by OpiesDad, to minimize ambiguity, avoid ActiveCell and the like.
Using Select will also slow down performance a lot compared to assigning to cells directly.
I'm pretty sure you need to escape quotes in Excel formulas inside of VBA by doubling the quotes, so a normal empty string becomes """". You also have Issue in single quotes in a formula, which I'm pretty sure will error in Excel; that should be in escaped double quotes as well.
I'm having a hard time figuring out what Range("E2:E" & Cells(Rows.Count, "C").End(xlUp).Row) actually does, but it sounds like you want to select E2 to the last used row of the sheet. Avoid Rows.Count or just generally referring to the rows of a sheet, as that will go to row 10^31. Use Worksheet.UsedRange to get the range from the first row and column with content to the last row and column with content. This also includes empty strings and can be a bit tricky sometimes, but is usually better than dealing with thousands of extra rows.
Also,
You don't need to use With if your only enclosing one statement, although it won't cause any problems.
I would not mix use of Range.Formula and Range.FormulaR1C1 unless you have a reason to.
Function Gre()
Dim ws as Worksheet
Set ws = ActiveSheet
Dim used as Range
Set used = ws.UsedRange
Dim lastRow as Integer
lastRow = used.Row + used.Rows.Count - 1
ws.Range("E2").Formula = "Under"
ws.Range("F2").Formula = "Over"
ws.Range("G2").Formula = "Result"
ws.Range("E2:E" & lastRow).Formula = "IF(C2<B2, C2-B2, """")"
ws.Range("F2:F" & lastRow).Formula = "IF(C2<B2, C2-B2, 0)"
ws.Range("G2:G" & lastRow).Formula = "IF(F2>0, ""Issue"", """")"
End Function
The first issue is the selecting of cells. This requires the macro to select the cell, then determine the cell address. If you need to actually select a cell, use Application.ScreenUpdating = False. Then the macro doesn't have to show the cursor selection of a cell. Dropping the select and incorporating the range into the formula assignment code line like below will gain some speed/efficiency.
Range("E2").FormulaR1C1 = "Under"
Range("E2:E" & Cells(Rows.Count, "C").End(xlUp).Row) is the code version of selecting the last cell in a blank column (row 1048576), then using the keystroke of ctrl and the up key to determine the lowest/last used cell. This gets you a row count of 1 every time since the column is blank. Since you're looking for the last row. It may be faster to count down from the top. My favorite method for this is a loop. Increment a variable within a loop, while looking for the last row. Then, the variable can be used instead of your bottom up strategy.
t = 0
Do Until Range("C2").Offset(t, 0).Value = ""
t = t + 1
Loop
With Range("E2:E" & t)
.Formula = "=IF(C2<B2,B2-C2,"""")"
End With`
Just like TSQL, quote characters need their own quote characters.
.Formula = "=IF(C2<B2,B2-C2,"""")"
The Range Fillup VBA function can be utilized in this case to fill all cells from the bottom with a common formula, accounting for Excel Formula Reference Relativity. The code below starts with the range that we got from the loop counter. Next, we set a variable equal to the total rows in Excel minus the row corresponding to the counter row. Then, we resize the original region by the necessary rows and use the FillDown function to copy the first formula down.
Here's the resulting code. This will fill the range starting from the last row in Excel.
Sub Gre()
Range("E2").FormulaR1C1 = "Under"
Range("F2").FormulaR1C1 = "Over"
Range("G2").FormulaR1C1 = "Result"
Do While Range("e2").Offset(t, 0).Value <> ""
t = t + 1
Loop
Range("E2").Offset(t, 0).Formula = "=IF(C2<B2,B2-C2,"""")"
r1 = Range("e2").EntireColumn.Rows.Count
r2 = Range("E2").Offset(t, 0).Row
Range("E2").Offset(t, 0).Resize(r1 - r2, 1).FillDown
Range("F2").Offset(t, 0).Formula = "=IF(C2>B2,C2-B2,0)"
Range("F2").Offset(t, 0).Resize(r1 - r2, 1).FillDown
Range("G2").Offset(t, 0).Formula = "=IF(F2>0,""Issue"","""")"
Range("G2").Offset(t, 0).Resize(r1 - r2, 1).FillDown
End Sub
As well as using double quotes you may need to use 0 in the first two formula otherwise they may evaluate to empty strings. This may give unexpected results for the last formula i.e. incorrectly return "Issue".
If you do not have blank columns between your data and the 3 new columns you can use CurrentRegion to determine the number of rows:
Range("E2:E" & Cells.CurrentRegion.Rows.Count).Formula = "=if(C2'<'B2,B2-C2,0)"
Range("F2:F" & Cells.CurrentRegion.Rows.Count).Formula = "=if(C2>B2,C2-B2,0)"
Range("G2:G" & Cells.CurrentRegion.Rows.Count).Formula = if(F2>0,""Issue"","""")"
Please try the following sample hope it will help you to wright formula in VBA
Sub NewEntry()
Dim last_row As Integer
Dim sht1 As Worksheet
Dim StockName As String
Set sht1 = Worksheets("FNO MW")
last_row = Cells.Find(What:="*", After:=Range("A1"), SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row
'MsgBox last_row
StockName = sht1.Cells(last_row, 1).Value
sht1.Cells(last_row, 1).Formula = "=RTD(""pi.rtdserver"", ,"" " & StockName & " "", ""TradingSymbol"")"
End Sub

Select Method of Worksheet Class Failed

I have this sub in Excel 2010 which is supposed to filter through all the cells in a sheet until it finds a match to Proj No, then paste a field from this row into another field.
When I try to run the sub, it gives me an error 1004: Select Method of Worksheet Class Failed. I've marked the line where this occurs. Any assistance would be greatly appreciated.
Option Explicit
Private Sub btnNext_Click()
Dim ProjNo As String
Dim Col As String
Dim Row As String
Dim cell As Range
Unload Dialog
formWait.Show
Sheets("Sheet7").Activate
ProjNo = Worksheets("Sheet1").Range("D6").Value
Col = Cells(Rows.Count, "A").End(xlUp).Row
For Each cell In Range("A2:A" & Col) 
If cell.Value = ProjNo Then
Row = Row & cell.Row
End If
Next cell
Workbooks("Form.xlsm").Sheets("Sheet7").Range("Row, 6").Copy Destination:=Sheets("Sheet1").Range("19, 5") ‘Error
Unload formWait
End Sub
I don't know what GWP is, but I think you want to use ProjNo there. The Range property doesn't accept an argument like that. Unless you have a named range of "Row,6" which you don't because it's not a legal name, you have to supply Range with a valid range reference, like A6 or D2:D12, for example.
Also, you can't concatenate rows and use them in a Range reference to get a larger range. You would have to copy each row inside the loop, union the ranges as you go, or better yet, filter on the value that you want and copy the visible rows.
Try this:
Private Sub btnNext_Click()
With ThisWorkbook.Worksheets("Sheet7")
'filter for the project id
.Range("A1", .Cells(.Rows.Count, 1).End(xlUp)).Resize(, 6).AutoFilter 1, "=" & .Range("D6").Value
'copy the visible rows
.Range("F2", .Cells(.Rows.Count, 6).End(xlUp)).SpecialCells(xlCellTypeVisible).Copy _
ThisWorkbook.Worksheets("Sheet1").Cells(19, 5)
'get rid of the filter
.AutoFilterMode = False
End With
End Sub
There are a few confusing items in your code above, so I wanted to place them long-form here. Let's get started:
Dim Col As String
Dim Row As String
It looks like your design expects these to be of type Long rather than type String. Even if these variables were meant to be strings, I would recommend adjusting their names -- when your fellow developer attempts to review your design, he or she is likely to see names like "Col" or "Row" and think "these are numbers". Easy fix:
Dim Col As Long, Row As Long
The next issue comes up here:
Col = Cells(Rows.Count, "A").End(xlUp).Row
The structure above is a common method for identifying the last ROW, not column. (It also appears that you have switched the "A" and number, which is another easy fix). While it is perfectly acceptable syntactically to name the variable for last row "Col", human users are likely to find this confusing. Identifying the last row (and the last col, which you use in the For Each loop), as explained in fantastic detail here, would be better handled like this:
Dim SheetSeven As Worksheet, SheetOne As Worksheet
Dim LastRow As Long, LastCol As Long
Set SheetSeven = ThisWorkbook.Worksheets("Sheet7")
Set SheetOne = ThisWorkbook.Worksheets("Sheet1")
With SheetSeven
LastRow = .Range("A" & .Rows.Count).End(xlUp).Row
LastCol = .Range("A" & .Columns.Count).End(xlToLeft).Column
End With
This should make your For Each loop look like this:
With SheetSeven
For Each cell in .Range("A2:A" & LastCol)
'... do you comparison and row incrementing here
Next cell
End With
Once you've identified your sheet as a variable, the Range.Copy action should be much easier as well:
With SheetSeven
.Range(.Cells(Row, 6)).Copy _
Destination:=SheetOne.Range(SheetOne.Cells(19, 5))
End With
Also one other thing you may wish to check is the status of Application.ScreenUpdating.
With the release of Office 2013 and later, a SDI (Single Document Interface) was introduced. If Application.ScreenUpdating is False and the workbook is not active, the implied call to Workbook.Activate will fail. Check the status of ScreenUpdating and set it to True if needed. You can set it back to False after the first Activate call for that workbook is made.
See this article:
https://support.microsoft.com/en-us/help/3083825/excel-workbook-is-not-activated-when-you-run-a-macro-that-calls-the-wo
In my case the error came as the sheet was hidden.
so I check if I am not working with the hidden sheet. Or you need to unhide the sheet before you try to select or activate sheet.
For Each sh In ThisWorkbook.Sheets
If Left(sh.Name, 8) <> "Template" Then
sh.Select
sh.Range("A1").Select
End If
Next

Counting non blank cell results without looping in Excel VBA -- e.g., with Specialcells

Here is the code I'm trying to count with in VBA, hoping to return
a count return variable of "3" from 'FormulaResultCount'. Why can't I
count what is visibly returned by the formulas within each cell; from the grey box (see photo below)?
Sub countNonBlanks()
Worksheets("Sheet1").Select
Range("C:C").Select
FormulaResultCount = Selection.SpecialCells(xlCellTypeFormulas).Count
'SpecialCells options from MSFT
' xlCellTypeAllFormatConditions. Cells of any format -4172
' xlCellTypeAllValidation. Cells having validation criteria -4174
' xlCellTypeBlanks. Empty cells 4
' xlCellTypeComments. Cells containing notes -4144
' xlCellTypeConstants. Cells containing constants 2
' xlCellTypeFormulas. Cells containing formulas -4123
' xlCellTypeLastCell. The last cell in the used range 11
' xlCellTypeSameFormatConditions. Cells having the same format -4173
' xlCellTypeSameValidation. Cells having the same validation -4175
' xlCellTypeVisible. All visible cells
'
End Sub
See formula as reference:
Note: Since I will have many more cells when working dynamically, loops will likely slow the process down too much. Also, I tried using CountA without result.
Maybe this:
FormulaResultCount = WorksheetFunction.CountIf(Range("C:C"), "?*")
Thus counting all cells in range that start with any character?
xlCellTypeFormulas. Cells containing formulas -4123
This would not return the cell based on their values but if they have any formula or not. As per your worksheet, you should get 5
Also, PLEASE PLEASE do not use .Select INTERESTING READ
Your code can also be written as
FormulaResultCount = Worksheets("Sheet1").Columns(3).SpecialCells(xlCellTypeFormulas).Count
Another Tip: When using SpecialCells, use appropriate error handling so that if there are no cells which match the SpecialCells criteria, your code won't break. See this example.
Sub Sample()
Dim ws As Worksheet
Dim Rng As Range
Set ws = ThisWorkbook.Sheets("Sheet1")
With ws
On Error Resume Next
Set Rng = .Columns(3).SpecialCells(xlCellTypeFormulas)
If Err <> 0 Then
MsgBox "No Cells with formulas were found"
Exit Sub
End If
On Error GoTo 0
End With
FormulaResultCount = Rng.Count
Debug.Print FormulaResultCount
End Sub
FOLLOWUP From Comments
Sub Sample()
Dim ws As Worksheet
Dim lRow As Long
Set ws = ThisWorkbook.Sheets("Sheet1")
With ws
lRow = .Range("A" & .Rows.Count).End(xlUp).Row
Debug.Print Evaluate("=COUNTA(C1:C" & lRow & _
")-COUNTIF(C1:C" & lRow & ","""")")
End With
End Sub
What you really might want is:
FormulaResultCount = Evaluate("CountA(C:C)")
I just learnt about the evaluate command. It's awesome!
And it gives you 3 :)
You can do this without VBA, using only formulas.
=ROWS(range)*COLUMNS(range)-COUNTBLANK(range)
If you're trying to do this in VBA, you can use this:
Function non_blank_cell_results_count(r As Range) As Long
non_blank_cell_results_count = r.Cells.Count - WorksheetFunction.CountBlank(r)
End Function

Excel macro to concatenate one row at a time to end of file

I need an Excel macro to join seven columns of data on each row until the end of the data is reached. For example if I have a formula like this:
=A1&B1&C1&D1&E1&F1&G1
How can I write the macro so that it increments for every row to the end of the file in a sequence like this?
=A1&B1&C1&D1&E1&F1&G1
=A2&B2&C2&D2&E2&F2&G2
=A3&B3&C3&D3&E3&F3&G3
With so many answers, the main focus on what assylias and I were highlighting has gone to waste :)
However, if you still want a VBA answer. Use this method. This is much faster than Looping or an Autofill.
Option Explicit
Sub Sample()
Dim LastRow As Long
Dim Ws As Worksheet
Set Ws = Sheets("Sheet1")
LastRow = Ws.Range("A" & Ws.Rows.Count).End(xlUp).Row
'~~> If your range doesn't have a header
Ws.Range("H1:H" & LastRow).Formula = "=A1&B1&C1&D1&E1&F1&G1"
'~~> If it does then
Ws.Range("H2:H" & LastRow).Formula = "=A2&B2&C2&D2&E2&F2&G2"
End Sub
If you have 1000's of rows then you might want to switch off Screenupdating and change Calculation to Manual before you run the code and then reset them at the end of the code.
I think the easiest way to do this would be to just fill down as assylias says but if you want to use VBA:
Selection.AutoFill Destination:=Range("Your Fill Range"), Type:=xlFillDefault
Should copy across the other rows.
I agree 100% with the comments and the other answers, why do you need VBA to do this, but just to answer your original question, this is how I would accomplish it:
Sub FillAllWithFormula()
Dim i As Variant
Dim wsht As Worksheet
'If you are using this for a specific Worksheet use the following
Set wsht = ThisWorkbook.Worksheets(yourWorksheetName)
'or if you are always using this for the active sheet use the following
Set wsht = ActiveSheet
For i = 1 To wsht.Rows.Count
'Replace "X" with the column letter you want your formula to appear in
wsht.Range("X" & i).Formula = "=A" & i & "&B" & i & "&C" & i & "&D" & i & "&E" & i & "&F" & i & "&G" & i
Next
Set wsht = Nothing
End Sub