Having an issue with using Application.Countifs to return a value from a worksheet. Here's the existing method to call it:
Application.CountIfs(sortws.Range(sortws.Cells(1, 3), sortws.Cells(sortws.Rows.Count, 3).End(xlUp)), _
CStr(sortws.Cells(x2, 3)), _
sortws.Range(sortws.Cells(1, 4), sortws.Cells(sortws.Rows.Count, 4).End(xlUp)), _
CStr(sortws.Cells(x2, 4)), _
sortws.Range(sortws.Cells(1, x3), sortws.Cells(sortws.Rows.Count, x3).End(xlUp)), _
Chr(34) & "<>" & Chr(34) & "&0")
In this case the two variables are iteration loops - x2 is equivalent to a row on the spreadsheet, x3 is a column.
When I Print the value, it returns as 0. When I print out the requisite addresses for each range and a string for the last argument, I get this:
=COUNTIFS($C$1:$C$1201,$C$1201,$D$1:$D$1201,$D$1201,$E$1:$E$1201,"<>"&0
Which, as far as I can tell, is an appropriate function. If I copy this out in it's entirety and plug it into a worksheet cell, I get the expected value of 1.
I've printed this formula in the VBA editor using EVALUATE and it returns the same value of 0 as I get with the original formula. I've also tried putting this in a cell next to the data by using this:
sortws.Cells(x2,7).Formula = "=COUNTIFS($C$1:$C$1201,$C$1201,$D$1:$D$1201,$D$1201,$E$1:$E$1201,"<>"&0"
This returns a Boolean value of TRUE, without the formula actually being in the worksheet cell.
If I remove the last bit of the function (that is checking for matches that don't equal 0) then it correctly adds up the total number of matches for the first two columns. As such, I think something is off with my syntax for checking for 0, but I can't determine what it is. All values in that column are numeric (technically currency, but formatted as general since it's just a blank worksheet). I'm struggling to udnerstand why the same formula works when plugged into a cell manually, however.
EDIT: Updating the formula with corrected quotes allows the EVALUATE function to run, as well as putting it in a cell with .Formula. This puts the updated formula as: "=COUNTIFS($C$1:$C$1201,$C$1201,$D$1:$D$1201,$D$1201,$E$1:$E$1201," & chr(34) & "<>" & chr(34) & "&0)", however the issue still remains with the original existing method returning 0, which uses the same method to return cells which don't equal 0 as the corrected quotes formula.
EDIT2: Resolved by #Jeeped, my existing VBA method was trying to replicate what would be required on a worksheet, simplifying the 0 check down to <>0 resolved it.
When supplying quotes within a quoted string, double them up.
sortws.Cells(x2, 7).Formula = "=COUNTIFS($C$1:$C$1201, $C$1201, $D$1:$D$1201, $D$1201, $E$1:$E$1201, ""<>0"")"
The full VBA Excel Application object COUNTIFS function should be written as follows.
Dim n As Long, x2 As Long, x3 As Long, sortws As Worksheet
Set sortws = Worksheets("sheet2")
x2 = 1201
x3 = 5
With sortws
n = Application.CountIfs(.Range(.Cells(1, 3), .Cells(.Rows.Count, 3).End(xlUp)), _
CStr(.Cells(x2, 3)), _
.Range(.Cells(1, 4), .Cells(.Rows.Count, 4).End(xlUp)), _
CStr(.Cells(x2, 4)), _
.Range(.Cells(1, x3), .Cells(.Rows.Count, x3).End(xlUp)), _
"<>0")
Debug.Print n
End With
Reduce your formulas down to the minimum required to avoid string concatenation mistakes and remember to close off your formula strings with a right bracket.
Related
I am working on a macro that will filter a database (updated daily) and compute specific formulas. Each time the macro is ran, a new sheet (uniquely named) will be created with the filtered information, and the calculations will be performed on an additional sheet.
I am having trouble creating a macro with the correct syntax. Each time the macro is run, the filtered data I need to reference is located on worksheet #3 (uniquely named). I'm new to VBA and don't understand the syntax I need to reference the worksheet(index) as the worksheet in an R1C1 formula. Right now, my code looks like this:
Dim LR As Long
LR = Worksheets(3).Cells(Rows.Count, 1).End(x1Up).Row
Range("G6").Select
ActiveCell.FormulaR1C1 = _
"=COUNTIF(='Worksheets(3)'!R6C5:R" & LR &"C5,R[-1]C"
The code is counting if a column of Years (of a variable length) is equal to R[-1]C, which is a cell that contains a certain year, and will display the count in cell G6.
Is it possible to use a worksheet(index) reference in this context? How else could I accomplish the task of referencing a worksheet without name? Would I need to reference a "name" variable?
You had an extra = sign in there and you seem to be missing a closing bracket but .Address external:=true produces a nice range reference.
Dim LR As Long, str as string
with Worksheets(3)
LR = .Cells(Rows.Count, 1).End(xlUp).Row
str = .range(.cells(6, 5), .cells(lr, 5)).address(external:=true, ReferenceStyle:=xlR1C1)
end with
Range("G6").FormulaR1C1 = _
"=COUNTIF(" & str & ", R[-1]C)"
Scenario: I have a code that should write a formula to a worksheet cells. This formula is for an API to retrieve some value. My formula is inside a loop (this is done for multiple columns) and references the first row for an identifier.
The original formula:
=FS(B1;"FI(DATE,,DATE)")
The modified formula with the floating reference (inside the loop):
For i = 1 To lColumn
If wb.Worksheets("Dates").Cells(i, 1).Value <> "" Then
wb.Worksheets("Dates").Cells(i,2).value = "=FS(" & i & "1;"FI(DATE,,DATE)")"
End If
Next i
Where lColumn is some pre-defined number.
Issue: I keep getting the "Unexpected end of statement" error in the formula part of the loop.
What I already tried: I tried different variations, repositioning the "s and 's, for example:
wb.Worksheets("Dates").Cells(i,2).value = "'"=FS(" & i & "1;"FI(DATE,,DATE)")""
or
wb.Worksheets("Dates").Cells(i,2).value = "'=FS(" & i & "1;"FI(DATE,,DATE)")"
or
wb.Worksheets("Dates").Cells(i,2).value = "'""=FS(" & i & "1;"FI(DATE,,DATE)")"
and so on. But the error still persists.
Question: What is the proper way to do this operation?
Working with formulas in VBA is a little bit tricky:
To write a formula, use the range.formula property, not the .value.
You have to write the formula as if you are using an english Excel. Parameter-separator is comma (not semicolon).
If a formula needs a quote, double it so that the VBA compiler understands that you want a quote within a string.
I find it helpfull to write a formula into a variable before assigning it - you can check in the debugger if it is exactly how it should before assigning it.
To check how the formula should look like, write it into a cell, change to the VBA-editor, open the immediate window and write ? activecell.formula
Try (untested as the formula you need is not valid to us):
with wb.Worksheets("Dates")
dim f as string, adr as string
adr = cells(i, 1).address(false, false) ' get rid of Dollar signs
f = "=FS(" & adr & ",""FI(DATE,,DATE)"")"
.Cells(i, 2).formula = f
end with
wb.Worksheets("Dates").Cells(i,2).formula = "=FS(" & Cells(1, i).Address(0,0) & ";""FI(DATE,,DATE)"")"
There may be a better way to convert the column number to a letter (which is the problem you are having, along with the double quotes)!
I am trying to use the Application.VLookup function in Visual Basic to find a value in a different workbook. However, whenever I use it, it always returns #N/A.
This is the layout of my function. LastRow() just returns the row number of the last row. SHORTAGE_SBT is a variable containing the source workbook name. SBT_Last is the last row of SHORTAGE_SBT. The ID that I'm searching with in in the B column, hence why I use "B" & ind to refer to it.
For ind = 4 To LastRow()
Range("H" & ind).Select
ActiveCell.Value = Application.VLookup("B" & ind, Workbooks(SHORTAGE_SBT).Sheets(1).Range("A14:DZ" & SBT_Last), Range("DZ1").Column, False)
Next
I have tried recording a macro for VLookup to see if it would help me understand the problem. The macro gave me this function, which worked but could not be used because it contains the hardcoded file name instead of using the variable.
Range("H" & ind).FormulaR1C1 = "=VLOOKUP(RC[-6],'[filename.xls]Sheet1'!R14C1:R2382C130, COLUMN(R[-3]C[122]), FALSE)"
I cannot see any significant difference between the way the macro lays out the arguments of the function as opposed to mine, other than using more direct references. I have tried using direct numbers in my code but doing so hasn't helped either.
Application.VLookup("B" & ind, Workbooks(SHORTAGE_SBT).Sheets(1).Range("A14:DZ" & SBT_Last), Range("DZ1").Column, False)
"B" & ind will be searched for "literally"; it will not be transformed into a range address because it is interpreted here by VBA, not by Excel. Try:
Application.VLookup(Range("B" & ind), Workbooks(SHORTAGE_SBT).Sheets(1).Range("A14:DZ" & SBT_Last), Range("DZ1").Column, False)
' ^^^^^^^^^^^^^^^^
Combine the two approaches:
Range("H" & ind).FormulaR1C1 = _
& "=VLOOKUP(RC[-6],'[" & Workbooks(SHORTAGE_SBT) _
& "]Sheet1'!R14C1:R2382C130, COLUMN(R[-3]C[122]), FALSE)"
I am trying to insert and change a counta formula based on the iteration of the for loop. The problem I am facing is that even though the code runs without error, nothing happens. The totalrow variable is consistently 0. I'm not sure where I am going wrong here. All the variable have been initialized outside the loop. CurrentColname is string, countUsedCols is long, totalRows is long.
For i = 2 To countUsedCols
ActiveSheet.Cells(3, i).Select
currentColName = ActiveSheet.Cells(3, i).Value
If currentColName = "Valid From" Then
totalrow = ActiveWorkbook.Sheets("Customer_Facing_View").Range("AR1").Formula = "=COUNTA($" & i & ":$" & i & ")"
totalrow = ActiveWorkbook.Sheets("Customer_Facing_View").Range("AR1").Value
A couple of things.
In vba when you put X=Y=Z X becomes a Boolean value based on whether y = z or not.
So when you write:
totalrow = ActiveWorkbook.Sheets("Customer_Facing_View").Range("AR1").Formula = "=COUNTA($" & i & ":$" & i & ")"
Totalrow is returning false because the formula in that cell is not the same as the string you have provided.
Second your COUNT A will resolve to a row not a column. i is a number and not a letter.
So remove the totalrow = from the first expression and change the range reference.
ActiveWorkbook.Sheets("Customer_Facing_View").Range("AR1").Formula = "=COUNTA(" & Columns(i).Address &")"
I guess you would like to write a formula into Cell AR1 to calculate the number of rows (with the first totalrow statement); and get that value back (with the second totalrow statement). But I believe the first totalrow statement is wrong as Scott indicated. You need change it.
Also for the Counta formula you need to include your original worksheet name otherwise it just count the column in the Customer_Facing_View worksheet.
I have a the following on my worksheet:
A cell that shows a currency [in A1]
A range of cells (two columns, one for the currency, and the other for a corresponding commission percentage) [defined as/named RANGE, and scoped to the worksheet]
A cell that [is trying] to determine the calculated commission percentage based on A1 and RANGE
I then have a VBA function called Calculate, as follows:
Function Calculate(LookupValue As Double, LookupRange As Range) As Double
Calculate = [VLOOKUP(LookupValue, LookupRange, 2)]
End Function
The cell that determines the percentage has the following:
=Calculate(A1, RANGE)
Problem is that the cell just returns #VALUE!...
Any idea what I could be doing wrong?
I have tried several things like type-hinting to Range(), passing LookupRange.Value2 to VLOOKUP, etc, none of which worked.
I have also tried to debug, noting that LookupRange does actually contain the range required in Value2, which is is why I tried to pass it to the function.
Side Note: The function and layout mentioned above is just a dummy - the actual function is somewhat more complex as it relies on negotiated rates, monthly margins, etc. This is why I'm using VBA in the first place. I know that I'm doing something wrong with the lookup, as it is the only thing that seems to be failing within the function - everything else corresponds and calculates.
From MSDN:
The advantage of using square brackets is that the code is shorter. The advantage of using Evaluate is that the argument is a string, so you can either construct the string in your code or use a Visual Basic variable.
in other words you can use
Calculate = [VLOOKUP(3, A1:B100, 2)]
but you can not use
LookupValue = 3
LookupRange = Range("A1:B100")
'or
'LookupRange = "A1:B100"
Calculate = [VLOOKUP(LookupValue, LookupRange, 2)]
What you can do is:
Option 1:
Function Calculate(LookupValue As Double, LookupRange As Range) As Double
Calculate = Evaluate("VLOOKUP(" & LookupValue & "," & LookupRange.Address & ", 2")
End Function
or better:
Function Calculate(LookupValue As Double, LookupRange As Range) As Double
Calculate = Evaluate("VLOOKUP(" & LookupValue & ",'" & _
LookupRange.Parent.Name & "'!" & LookupRange.Address & ", 2")
End Function
However I suggest:
Option 2:
Function Calculate(LookupValue As Double, LookupRange As Range) As Double
Calculate = Application.VLookup(LookupValue, LookupRange, 2)
End Function
I hope you know about meaning of 4th parameter:
If TRUE or omitted, an exact or approximate match is returned. If an exact match is not found, the next largest value that is less than lookup_value is returned.
The values in the first column of table_array must be placed in ascending sort order; otherwise, VLOOKUP may not give the correct value. You can put the values in ascending order by choosing the Sort command from the Data menu and selecting Ascending.
Btw, Calculate is not good name for UDF, since VBA already has function Application.Calculate. I'd rename it to avoid confusion.