In VBA, is the syntax different when creating an object vs. not? - vba

Apologies, this was difficult to word as a clear question.
The following line is valid:
objWord.Documents.Add Template:=templatePath
But this line is not:
Set objMergedReq = objWord.Documents.Add Template:=templatePath
I get the following compiler error:
Expected: end of statement
Why are the two interpreted differently? How do I eliminate the error? Do I need extra parentheses in case 2?

The crux is this:
objWord.Documents.Add Template:=templatePath
This is a function call, but the returned value (an object reference) is discarded and thus, the function is really used as if it were a procedure; with VBA's implicit procedure call syntax, parentheses are not present. You can use the [deprecated] explicit call syntax to require the parentheses:
Call objWord.Documents.Add(Template:=templatePath)
As you noticed, this is illegal:
Set objMergedReq = objWord.Documents.Add Template:=templatePath
Because the syntax for a function call (you're not discarding the returned value here) requires the parentheses whenever an argument list is specified:
Set objMergedReq = objWord.Documents.Add(Template:=templatePath)
If you're tempted to "just use parentheses everywhere", know that you'll run into other syntax issues, as soon as you need to specify 2 or more arguments:
MsgBox (message, vbOkOnly + vbInformation) 'illegal
That's because when arguments are surrounded by parentheses, you're really telling VBA to evaluate the contents of the parentheses as a value, and pass the result ByVal to the function/procedure, even if that function/procedure is explicitly specifying the parameter as ByRef.

you need to use parenthesis when setting, so set x=f(y)

When there is no return value, like when you use a Sub, or you ignore the return value, you don't put the parameters in parenthesis. Optionally, you can use "Call" and then put the parameters in parenthesis.
In your example,
objWord.Documents.Add Template:=templatePath
the add method does create a return value, but you are not using it. Therefore, you either don't use parentheses (as you show), or you could use the "Call" statement:
Call objWord.Documents.Add(Template:=templatePath)
In your second example, you are using the return value, so parenthesis are required:
Set objMergedReq = objWord.Documents.Add(Template:=templatePath)
The "Set" is needed because you are assigning an object. The same rules apply if the return value is a non object -- you would just omit the "Set".

Related

SQL ''Field" = {variable}' Select by attribute arcpy

I am trying to run a select by attribute where I select all points where "Id" field matches the numeric variable point_id. point_id = 375.
I've tried a few quotation styles and using curly brackets to call my variable. I'm not the most familiar with SQL queries and get an error saying the positional argument follows the keyword string. I have also tried storing my SQL as a variable on it's own called a whereClause and get the same error.
First attempt code
arcpy.management.SelectLayerByAttribute(in_layer_or_view = deer,
selection_type = "NEW_SELECTION",
f'"Id"={point_id}')
Second attempt code
The is a Python issue, not related to ArcGIS or SQL.
You are trying to pass three arguments. For the first two arguments you're using keyword argument (explicitly specifying the argument name: in_layer_or_view = deer), but for the third one you're using positional argument (letting python assign the value to the appropriate argument based on the order of the arguments).
The execption you're getting is telling you that you can't mix the two types this way. Once you started using keyword arguments in the function call, all of the next argument must be passed with their explicit name too.
To fix this, you can use positional argument for all of the arguments (i.e. not specifing argument names at all), or alternatively keep specifing the names for all of the rest of the arguments.
In your case, this should work:
arcpy.management.SelectLayerByAttribute(in_layer_or_view=deer,
selection_type="NEW_SELECTION",
where_clause=f'"Id"={point_id}')
or alternatively:
arcpy.management.SelectLayerByAttribute(deer,
"NEW_SELECTION",
f'"Id"={point_id}')

Format - Expected Array

I keep getting an error when I try to format this number. I've done it in VBA before and I tried to change the SOPID to a variant, a string, and an integer.
Dim SOPID As Integer
SOPID = DMax("file_id", "tblSOP") + 1
'Output test data
MsgBox (format(SOPID, "000"))
I have no idea what I am doing wrong.
Assuming the code was pasted directly from your IDE, the casing of format is suspicious; that would be Format, unless there's a format variable or function that's in-scope, ...and that's being invoked here.
Look for a public (could be implicitly Public, or if it's in the same module then it could also be Private) format function procedure that expects an array argument: that's very likely what's being invoked here.
Rubberduck (free, open-source; I manage this project) can help you easily identify exactly what's being invoked and an inspection would tell you about any shadowed declarations, but to be sure you can fully-qualify the function call to avoid inadvertently invoking another function that's in scope:
MsgBox VBA.Strings.Format$(SOPID, "000")
Note that there are no parentheses around the argument list of a parameterized procedure call in VBA; the parentheses you have there are surrounding the first argument and making the expression be evaluated as a value that is then passed to the invoked function: this isn't necessary.
Also note the $: the VBA.Strings.Format function takes and returns a Variant; VBA.Strings.Format$ takes and returns a String. If you aren't dealing with any Null values (an Integer couldn't be Null), consider using the String-returning alias.

Conditional Statement Returns Exception Error

I have this conditional statement
loRecordFieldData = CType(IIf(loRecordsAttributeCollection.ContainsKey(loMappingObject.FieldID), _
loRecordsAttributeCollection(loMappingObject.FieldID), Nothing)
the problem is when loRecordsAttributeCollection doesn't contain the FieldID it return exception error key not found instead of nothing.
Could anyone explain this and how to prevent it?
Thank you
IIf is a function, so all of its arguments will be evaluated before it’s called. Use If instead, which is a genuine inline conditional and won’t evaluate the operand that isn’t returned:
loRecordFieldData = CType(If(loRecordsAttributeCollection.ContainsKey(loMappingObject.FieldID), _
loRecordsAttributeCollection(loMappingObject.FieldID), Nothing)
If this is a dictionary, you can also use TryGetValue or wrap it in an extension method:
Dim loRecordFieldData As … ' the value type of loRecordsAttributeCollection
loRecordsAttributeCollection.TryGetValue(loMappingObject.FieldID, loRecordFieldData)
' now cast
Use If instead. IIf syntax is obsolete in vb.net

Meaning of `:=` Syntax in VBA methods

When writing the following VBA, what is the root cause of the error "Expected =" given that we are using the Format:=2.
Workbook.Open (filename, Format:=2)
I understand that this format works when setting the variable as in the following code, but why does it work here and not in the above format?
Set wrkb = Workbook.Open (filename, Format:=2)
Also what is this operator called, := and how is it used?
It's not an operator, it's a named argument.
You can chose the order of the arguments/parameters by directly specifying what they are.
The concept of named arguments also exists in multiple modern languages, such as c# (its optional, just like in VBA) and swift (by default it's required, but you can disable it).
Named arguments also allow you to omit arguments that are optional altogether, but pass an argument that is further back in the list of arguments. A good way to try named arguments out is the messagebox, since it has many optional arguments with default values.
Example: MsgBox only specifying title:
MsgBox Title:="wew lad"
Or, in the more modern way of writing vb(a) code:
Call MsgBox(Title:="wew lad")
The MsgBox is a good example, since you can call it normally, and then specify a parameter further back directly (works with other methods too):
Call MsgBox("Yes!", Title:="wew lad")
Once there are named parameters, you can't add ordered parameters after:
'Call MsgBox (Prompt:="Yes!", vbOkCancel Title:="wew lad")
'this doesnt work!
Now, why does this raise an error:
MsgBox ("Hello", Title:="test")
This is some of the weirder parts of vba. Calling functions with return values but ignoring the value while using parentheses is a bit broken.
You can circumvent this by adding a Call in front of it (vba then knows it can ignore the result).
This would look like this:
Call MsgBox ("Hello", Title:="test")
Honestly, I don't know why this is, but I've had it cause really weird bugs. When you use the parentheses syntax, I can really recommend using the Call keyword before the method call.
As Macro Man mentioned, you can also omit the parentheses, instead of using Call:
MsgBox "Hello", Title:="test"
And it will work, too. This was the original vba coding style and isn't really used anymore.

Why is the syntax for a worksheet IF() function with optional parameters different from UDF?

Never really deeply thought about this but why does an =IF() function require you to pass commas even though the last 2 parameters are Optional?
=IF(logical_test, [value_if_true], [value_if_false])
Why can't we just call it
=IF(TRUE)
to get TRUE (default return value when logical_test evaluated to TRUE)
we have to call it providing the two commas in between even when leaving the parameters missing/empty...
=IF(TRUE, ,)
In a UDF when using Optional Parameters we will not need to pass the commas (much simpler version of the original IF, error handling not implemented as not needed here)
Public Function MYIF(logical_test, Optional value_if_true$ = "TRUE", Optional value_if_false$ = "FALSE")
MYIF = IIf(Evaluate(logical_test), value_if_true, value_if_false)
End Function
and when calling from a spreadsheet we can omit the commas
=MYIF(TRUE)
Anyone, besides Spolsky ;), is aware of the reason why do we need to provide commas when using the native =IF()?
I am in Excel 2010 but just have noticed that 2007 requires 2 parameters.. Still, in 2010 the function definition shows 1st as required, last 2 optional. Any explanation?
Update:
I am aware of the =1=1 and =AND(TRUE,FALSE) syntax, I am not looking for an alternative to a short-hand version of the =IF(). Instead what I am asking is
why do the commas need to be provided with the original =IF() since the last 2 parameters are Optional Parameters?
"Optional argument" can apparently have two distinct meanings for Excel functions:
the optional argument may be omitted;
the value of the optional argument may be omitted.
Sometimes both are allowed, sometimes only #2 is allowed.
Look at the documentation for IF:
IF(logical_test, [value_if_true], [value_if_false])
[...]
value_if_false Optional. [...]
If logical_test evaluates to FALSE and the value_if_false argument is omitted [...], the IF function returns the logical value FALSE.
If logical_test evaluates to FALSE and the value of the value_if_false argument is omitted [...], the IF function returns the value 0 (zero).
Okay, both are allowed. So =IF(FALSE,) (argument omitted) returns FALSE, while =IF(FALSE,,) (argument there but value omitted) returns 0.
But for the value_if_true argument, only option #2 is allowed according to the documentation. There is no provision for omitting this argument entirely; only omitting its value is supported. So =IF(TRUE,) and =IF(TRUE,,) (argument there but value omitted) both return 0, while =IF(TRUE) (argument omitted entirely) is unsupported and therefore does not compile.
The above describes the documented and supported behaviour. Of course this doesn't answer the deeper question of "why" it is like that. My answer to that is, I don't know. I'm sure the makers of Excel had a good reason, or at least thought they did.
As for UDFs, they are written in VBA and their Optional parameters are governed by VBA's rules, which are different. There is no such thing as omitting "the value of" a parameter in VBA functions.