Help:Conditional expressions

From the AARoads Wiki: Read about the road before you go
Jump to navigation Jump to search

This page describes how to use various parser functions to display different results based on detected conditions in a page or template.

Here is a quick summary of these functions in terms of how they treat their input (the function names are linked to more detailed descriptions of them found below on this page):

  • #if checks the truth value of a string
  • #ifeq checks whether two strings or numbers are equal
  • #switch compares a string to a set of possible values
  • #expr evaluates a mathematical expression
  • #ifexpr evaluates a mathematical expression and acts on the truth value of the result
  • #iferror checks whether a string (often an expression inside of #expr) triggers a parser error
  • #ifexist checks whether a page with a given title exists on the wiki (including image/media files)

For the use of these functions in tables, see Help:Conditional tables.

Summary

The basic syntax (and use) of each function is as follows:

  • {{#if: test string | value if true | value if false }}
    (selects one of two values based on whether the test string is true or false)
  • {{#ifeq: string 1 | string 2 | value if equal | value if unequal }}
    (selects one of two values based on whether the two strings are equal—a numerical comparison is done whenever that is possible)
  • {{#switch: test string | case1 = value for case 1 | ... | default }}
    (chooses between multiple alternatives based on the value of the test string—basically equivalent to a chain of #ifeq tests, but much more efficient)
  • {{#expr: expression }}
    (evaluates a given expression; see Help:Calculation for details)
  • {{#ifexpr: expression | value if true | value if false }}
    (selects one of two values based on the truth value of an evaluated expression)
  • {{#iferror: test string | value if error | value if no error }}
    (selects one of two values based on whether the test string generates a parser error)
  • {{#ifexist: page title | value if exists | value if doesn't exist }}
    (selects one of two values depending on the existence of a page having the given title)

Note that "truth" is interpreted very differently by the string-based #if function and the numerically oriented functions #expr and #ifexpr. A string is considered true if it contains at least one non-whitespace character (thus, for example, the #if function interprets the strings "0" and "FALSE" as true values, not false). Any string containing only whitespace or no characters at all will be treated as false (thus #if interprets " " and "", as well as undefined parameters, as false values). On the other hand, in the expressions evaluated by #expr and #ifexpr, Boolean operators like and, or, and not interpret the numerical value 0 as false and any other number as true. In terms of output, Boolean operations return 1 for a true value and 0 for false (and these are treated as ordinary numbers by the numerical operators). Non-numerical strings (including most uses of empty strings and undefined parameters) will cause #expr and #ifexpr to report an error.

Also note that all leading and trailing whitespace within each of the parts of a parser function call gets stripped out, allowing the calls to be formatted with extra whitespace for better readability. For example:

{{#if: {{{xx|}}}
   | parameter xx value is true
   | parameter xx value is false
}}

Here, only the spaces in the strings "parameter xx value is true" and "parameter xx value is false" are significant. All other whitespace is ignored. Thus, the construct above is equivalent to the following call:

{{#if:{{{xx|}}}|parameter xx value is true|parameter xx value is false}}

In any part of a parser function call where a string is expected, one can use a literal string, a template call, a parser function call, or some other magic word.

Using #if

See also: the {{if}} template

The #if function selects one of two alternatives based on the truth value of a test string.

{{#if: test string | value if true | value if false }}

As explained above, a string is considered true if it contains at least one non-whitespace character. Any string containing only whitespace or no characters at all will be treated as false.

Undefined parameter values are tricky: if the first positional parameter was not defined in the template call, then {{{1}}} will evaluate to the literal string "{{{1}}}" (i.e., the 7-character string containing three sets of curly braces around the number 1), which is a true value. (This problem exists for both named and positional parameters.) But {{{1|}}} will evaluate to the empty string (a false value) because the vertical bar or pipe character, "|", immediately following the parameter name specifies a default value (here an empty string because there is nothing between the pipe and the first closing curly brace) as a "fallback" value to be used if the parameter is undefined.

This function can be used to check whether a parameter has been passed to a template with a true value.

Examples:

{{#if: {{{1|}}}
   | first positional parameter has a true value
   | first positional parameter has a false value (or no value)
}}

{{#if: {{{xx|}}}
   | named parameter xx has a true value
   | named parameter xx has a false value (or no value)
}}

{{#if: {{{xx|}}}{{{yy|}}}
   | either xx or yy has a true value, or both are true
   | both xx and yy have a false value (or no value)
}}

Testing whether both of two parameters are true requires a little more effort. For example:

{{#if: {{{xx|}}}
   | {{#if: {{{yy|}}}
       | both xx and yy have true values
       | xx is true but not yy
     }}
   | both xx and yy are false
}}

Note that nesting #if functions like this gets very resource-intensive very fast. On some wikis, even seven levels of nesting might exceed resource limits.

See the discussion of #ifeq for how to detect whether a parameter has been defined regardless of whether its value is true or false.

Using #ifeq

See also: the {{ifeq}} template

The #ifeq function selects one of two alternatives based on whether two test strings are equal to each other.

{{#ifeq: string 1 | string 2 | value if equal | value if not equal }}

If both strings are valid numerical values, they are compared as numbers, rather than as literal strings:

{{#ifeq: 01 | 1 | equal | not equal }}equal
{{#ifeq: x01 | x1 | equal | not equal }}not equal
{{#ifeq: 2.000 | 002 | equal | not equal }}equal
{{#ifeq: 2.5 | 2+.5 | equal | not equal }}not equal (arithmetic!)
{{#ifeq: 2*10^3 | 2000 | equal | not equal }}not equal (arithmetic!)
{{#ifeq: 2E3 | 2000 | equal | not equal }}equal

As seen in the 4th and 5th examples, mathematical expressions are not evaluated. They are treated as regular strings. But #expr can be used to evaluate such expressions.

{{#ifeq: {{#expr:2*10^3}} | 2000 | equal | not equal }}equal

String comparisons are case-sensitive:

{{#ifeq: King | king | equal | not equal }}not equal

For case-insensitive checking, use the {{lc:}} or {{uc:}} function to force the strings to all lower- or upper-case. This is most useful when dealing with parameter values:

{{#ifeq: {{lc:King}} | king | equal | not equal }}equal
{{#ifeq: {{lc: {{{position}}} }} | top | code if true | code if false }}

In the second example, the values "top", "Top", and "TOP" will all result in successful matches.

This parser function can be used to detect whether a template parameter is defined, even if it has been set to a false value. For example, to check whether the first positional parameter has been passed to a template (note that the strings "+" and "-" can be any two different non-whitespace strings):

{{#ifeq: {{{1|+}}} | {{{1|-}}} | 1 is defined | 1 is undefined }}

To be specific, here's what this code generates when called in the following ways:

{{template-name}}1 is undefined
{{template-name|}}1 is defined
{{template-name|1=}}1 is defined
{{template-name|1=foo}}1 is defined

See mw:Help:Extension:ParserFunctions##ifeq for more details, including possible counterintuitive results due to the way this function is implemented.

Using #switch

The #switch function selects between multiple alternatives based on an input string.

{{#switch: test string | case1 = value for case 1 | ... | default value }}

Equivalent to the switch statement found in some programming languages, it is a convenient way of dealing with multiple cases without having to chain lots of #if functions together. However, note that performance suffers when there are more than 100 alternatives. Placing common values earlier in the list of cases can cause the function to execute significantly faster.

For each case, either side of the equals sign "=" can be a simple string, a call to a parser function (including #expr to evaulate expressions), or a template call. If any cases are not associated with a value (i.e., no equals sign is used), the next specified value will be used. This allows multiple cases to share the same value without having to specify that value repeatedly (as seen in the example below).

If no matching case is found, then the default value is used. This is usually specified last with no associated "case" value, as seen in the syntax summary above, but it can also be specified at any point after the test string if the construct | #default = value is used (see the second example below). If no default is specified in either way, then a null string will be returned when no cases match the input string.

This function in particular benefits from being set up in a multiline format.

{{#switch: {{{x|}}}
 | 1 = one
 | 2 = two
 | 3 = three
 | 4 = four
 | 5
 | 6
 | 7 = in the range 5 to 7
 | other
}}

Here, if the value of the template parameter x is the string "1", then the output will be the string "one"; if "2", then "two"; and so forth. But for any of the values "5", "6" or "7", the output will be the string "in the range 5 to 7". For any other value, or a null value, it will return the string "other".

The following example is equivalent to the previous one:

{{#switch: {{{x|}}}
 | #default = other
 | 1 = one
 | 2 = two
 | 3 = three
 | 4 = four
 | 5|6|7 = in the range 5 to 7
}}

See Help:Switch parser function for a full description and more examples.

Using #expr

The #expr function evaluates mathematical (including Boolean) expressions. While not itself a conditional function, it is often used inside of those functions, so it is briefly described here. See m:Help:Calculation for further details.

{{#expr: expression }}

Unlike the #if function, all values in the expression evaluated by #expr are assumed to be numerical. It does not work with arbitrary strings. For the purpose of its Boolean operations (e.g., and, or, and not), false is represented by 0 and true by 1; these values are treated as regular numbers by the numerical operators. As input, the Boolean operators treat 0 as false and every other number as true. If any non-numerical strings are used, an error will result.

Examples:

{{#expr: ( {{{1}}}+{{{xshift}}} - 6 ) * 18.4 }}
{{#expr: ln(7)^3 - abs(-0.344) + floor(5/3) round 3 }}
{{#expr: {{{n}}}>0 and {{{n}}}<1.0 }}

Note that these examples assume that all parameters referred to are defined and have numerical values. If this is not the case, errors will occur. See #iferror for one way to handle errors.

Using #ifexpr

The #ifexpr function evaluates an expression in exactly the same way #expr does (see the explanation of that function for details), but returns one of two possible values depending on the truth value of the result.

{{#ifexpr: expression | value if true | value if false }}

Examples:

{{#ifexpr: ( {{{1}}} + {{{2}}} ) * 2.63 > 45 | above 45 | not above 45 }}
{{#ifexpr: {{{1}}} > 0 and {{{1}}} < 1.0 or {{#ifeq:{{{decimal}}}|yes}} | is decimal | not decimal }}

As explained above, for the purposes of this function, 0 is false and any other numerical value is true.

{{#ifexpr: 0 | yes | no }}no
{{#ifexpr: 1 | yes | no }}yes
{{#ifexpr: 2 | yes | no }}yes

An empty input expression evaluates to false.

{{#ifexpr: | yes | no }}no

But invalid input, including input containing non-numerical strings, will display an error message.

{{#ifexpr: = | yes | no }}Expression error: Unexpected = operator.
{{#ifexpr: A=B | yes | no }}Expression error: Unrecognized word "a".

Under normal circumstances, the function #ifexpr is equivalent to using the following construct with #ifeq and #expr (note the reversal of the "true" and "false" output values, since we are comparing the input result to "0"):

{{#ifeq: {{#expr: expression }} | 0 | value if false | value if true }}

But any error message generated by #expr will be seen by #ifeq as an ordinary string that is not equal to "0", and thus the value if true will be output.

{{#ifeq: {{#expr: = }} | 0 | yes | no }}no

Either or both of the output values may be omitted (or left empty), in which case (assuming the "missing" branch of the construct is reached) no output results.

{{#ifexpr: 1 > 0 | yes }}yes
{{#ifexpr: 1 > 0 | | no }}

See mw:Help:Extension:ParserFunctions##ifexpr for more details, including possible counterintuitive results due to the way this function is implemented.

Using #iferror

The #iferror function selects one of two alternatives depending on whether its input triggers an error.

{{#iferror: test string | value if error | value if correct }}

The test string is typically a call to #expr or some other parser function, but can be plain text or a template call, in which case an error would be triggered by any HTML object with class="error" or any of various template errors such as loops and recursions, or some other "failsoft" parser error.

One or both of the return values can be omitted. If the value if correct is omitted, the test string is returned if it is not erroneous. If the value if error is also omitted, an empty string is returned on an error:

{{#iferror: {{#expr: 1 + 2 }} | error | correct }}correct
{{#iferror: {{#expr: 1 + X }} | error | correct }}error
{{#iferror: {{#expr: 1 + 2 }} | error }}3
{{#iferror: {{#expr: 1 + X }} | error }}error
{{#iferror: {{#expr: 1 + 2 }} }}3
{{#iferror: {{#expr: 1 + X }} }}
{{#iferror: {{#expr: . }} | error | correct }}correct
{{#iferror: <strong class="error">a</strong> | error | correct }}error

Using #ifexist

The #ifexist function selects one of two alternatives depending on whether a page exists at the specified title.

{{#ifexist: page title | value if page exists | value if page doesn't exist }}

The page can be in any namespace, so it can be an article or "content page", an image or other media file, a category, etc. The actual content of the page is irrelevant, so it may be empty or a redirect. Titles that would result in redlinks do not exist (and, as with redlinks, checking for the existence of a nonexistent page causes the title to appear on Special:WantedPages).

The checking is extremely fast, but has been limited to 500 instances per page because it is considered an "expensive parser function". (However, multiple checks of the same title on the same page do not count as multiple instances, because the results of the first check is cached and reused for the subsequent checks.)

Checking for template parameters

A common idiom encountered in template coding is the "chain of fallback values", as seen in this example:

{{{1|{{{url|{{{URL|}}}}}}}}}

Here, if the first positional parameter is defined, then its value will be used. If it is undefined, then the parameter named url will be checked and if it is defined, then its value will be used. If both the first positional parameter and url are undefined, then the parameter named URL is checked: if it is defined, its value is used; if not, then the empty string will be used.

The problem is, this construct tends to be interpreted as being equivalent to:

{{#if: {{{1|}}} | {{{1}}} | {{#if: {{{url|}}} | {{{url}}} | {{#if: {{{URL|}}} | {{{URL}}} }} }} }}

and it is not. The difference is that the first construct, using default values, depends on the definedness of the parameters, whereas the second construct, using #if functions, depends on the truth value of the parameters. These are two very different things.

If any of the parameters in the chain of fallback values (in the first construct) have been set to an empty value (or only whitespace) in the template call, then that empty value will be used instead of "falling back" to the next parameter in the chain. So, for example, if the template is called in either of these two ways:

{{template-name||url=https://example.com/}}
{{template-name|url=https://example.com/|}}

The first positional parameter has been defined: its value is the empty string. Thus, the value of url is irrelevant. The template never "gets to" that value.

Similarly, if the template is called in this way:

{{template-name|url=|URL=https://example.com/}}

The empty value of url will be used instead of the value of URL.

These examples might seem a bit contrived, since they rely on the template being called in "the wrong way". But it is surprisingly easy to run across cases where these problems occur with perfectly reasonable template calls (especially if there is a "hierarchy" of templates, where one template calls another, passing on the values of its parameters to the parameters of the "lower-level" template—in such cases, one will often end up with defined-but-empty parameter values).

Because of issues like this, template coders sometimes find themselves needing to distinguish between different combinations of three states: defined-and-non-blank, defined-and-blank, and undefined. This can be done in the following ways (the first positional parameter is used here, but named parameters work the same way).

Defined-and-non-blank   vs.   undefined or defined-and-blank
{{#if: {{{1|}}} | 1 is defined and contains non-whitespace | 1 is undefined, empty, or contains only whitespace }}
Defined (whether non-blank or blank)   vs.   undefined
The following are equivalent alternatives:
  • {{#ifeq: {{{1|+}}} | {{{1|-}}} | 1 is defined (and possibly empty or only-whitespace) | 1 is undefined }}
  • {{#ifeq: {{{1|/}}}{{{1|}}} | / | 1 is undefined | 1 is defined (and possibly empty or only-whitespace) }}
  • {{#if: {{#if: {{{1|/}}} | {{{1|}}} | / }} | 1 is defined (and possibly empty or only-whitespace) | 1 is undefined }}

Note that the + and - characters can be any two different non-whitespace characters. Also, if you just want to use the value of the parameter when it's defined and some other value when it's undefined, you can use the simpler "fallback" construct:

{{{1|some other value}}}
Defined-and-non-blank   vs.   defined-and-blank   vs.   undefined
The following are equivalent alternatives:
  • {{#ifeq: {{{1|+}}} | {{{1|-}}} | 1 is defined {{#if: {{{1|}}} | and non-blank | and blank }} | 1 undefined }}
  • {{#if: {{{1|/}}} | {{#if:{{{1|}}} | 1 is defined and non-blank | 1 is undefined }} | 1 is defined and blank }}

If you don't care about the undefined case, you can remove the "|1 undefined" in both examples.

Variadic templates

Wikitext syntax does not allow natively to create truly variadic templates, but only pseudo-variadic ones, that check the incoming parameters one by one until a certain fixed amount. It is possible to break this limitation however by using dedicated modules. For simple cases, {{#invoke:separated entries|main}} allows to expand all sequential parameters blindly and has the ability to set custom delimiters. For more complex cases, {{#invoke:params}} allows to count, list, map, filter and propagate all incoming parameters without knowing their number in advance.

See also