Runtime verified Test scripts included

Syntax

IIf(expression, trueValue, falseValue)  →  string
3 arguments — exactly

Parameters

Name Type Required Description
expression boolean Yes Boolean expression to evaluate — a non-boolean value always selects the false branch
trueValue string | number | boolean | date Yes Value returned when true; evaluated only when true is selected
falseValue string | number | boolean | date Yes Value returned when false; evaluated only when false is selected

Example

The condition has to be a real comparison or a boolean-returning function:

%%[
  VAR @firstName
  SET @firstName = AttributeValue("FirstName")
]%%
Hello %%=IIf(Empty(@firstName), "there", @firstName)=%%,

Passing the value itself does not work. This renders anonymous even when the name is present, because a plain string never selects the true branch:

%%=IIf(@firstName, @firstName, "anonymous")=%%

Because only the selected branch runs, an expensive or fragile call is safe in the branch that is not taken:

%%=IIf(Empty(@key), "no lookup needed", Lookup("Preferences", "Tier", "SubscriberKey", @key))=%%

Return value

string — whichever branch argument was selected, returned unconverted.

The domain is whatever the caller passes, so there is no set of literals to test for. The run rendered T, F, 42 and a full date-time string from this function.

Behaviour

A comparison selects correctly in both directions. 1 == 1 took the true branch, 1 == 2 the false one. A boolean-returning function works as the condition too, including negated with NOT.

There is no truthiness. Eight non-boolean conditions — an undeclared variable, a variable declared but never assigned, the empty string, three spaces, 0, "0", "false" and the plain string hello — every one selected the false branch. Since the references type the first parameter as a string, writing IIf(@name, ...) and expecting a non-empty name to be true is an easy mistake, and it fails silently.

Branch values pass through unconverted. A number came back as 42, a date as its full date-time string.

Only the selected branch is evaluated

This is the most useful undocumented property of the function, and it needed a control to prove.

A call that reliably aborts the page was parked in the branch that should not be taken — in both directions. Both requests returned HTTP 200 with the other branch’s value and both markers printed. On its own that only shows nothing bad happened; so the same deploy also called that aborting function directly in its own branch, and that request returned HTTP 422 with no output at all. The abort was reachable, and simply was never reached.

Neither reference says anything about evaluation order, so this is undocumented rather than contradicted. Practically it means a Lookup, a HTTPGet or any other costly call can sit in a branch guarded by the condition, and it will not run unless it is the answer.

How the four Utility tests compare

The same inputs put through all four functions, on one page, in one run:

Input Empty IsNull IsNullDefault(x, "DEF") IIf(x, "T", "F")
undeclared variable True False (empty) F
declared, never set True False (empty) F
"" True False (empty) F
" " False False ` ` F
0 False False 0 F
"0" False False 0 F
"false" False False false F
"hello" False False hello F

The last column is constant, which is the point: feed this function a value and it always answers false. Feed it Empty(value) — the first column — and it answers the question that was actually meant.

Show test script
%%[
  VAR @b, @unset, @es, @sp, @z, @z0, @sf, @val
  SET @b = RequestParameter("b")
  SET @es = ""
  SET @sp = "   "
  SET @z = 0
  SET @z0 = "0"
  SET @sf = "false"
  SET @val = "hello"

  /* known-good control: renders on every request, so a run of HTTP 422s
     can be told apart from a broken deploy */
  OutputLine(Concat("CTRL=[", Uppercase("ok"), "]"))

  /* comparisons and boolean-returning functions in the condition slot */
  IF @b == "f_bool" THEN
    OutputLine(Concat("--- f_bool start ---"))
    OutputLine(Concat("F_EXPT=[", IIf(1 == 1, "T", "F"), "]"))
    OutputLine(Concat("F_EXPF=[", IIf(1 == 2, "T", "F"), "]"))
    OutputLine(Concat("F_FN=[", IIf(Empty(@es), "T", "F"), "]"))
    OutputLine(Concat("F_NOT=[", IIf(NOT Empty(@es), "T", "F"), "]"))
    OutputLine(Concat("--- f_bool done ---"))
  ENDIF

  /* a name that was never declared, in the condition slot */
  IF @b == "f_und" THEN
    OutputLine(Concat("--- f_und start ---"))
    OutputLine(Concat("F_UND=[", IIf(@zzz_never_declared, "T", "F"), "]"))
    OutputLine(Concat("--- f_und done ---"))
  ENDIF

  /* declared with VAR, never assigned */
  IF @b == "f_unset" THEN
    OutputLine(Concat("--- f_unset start ---"))
    OutputLine(Concat("F_UNSET=[", IIf(@unset, "T", "F"), "]"))
    OutputLine(Concat("--- f_unset done ---"))
  ENDIF

  /* the explicitly empty string */
  IF @b == "f_es" THEN
    OutputLine(Concat("--- f_es start ---"))
    OutputLine(Concat("F_ES=[", IIf(@es, "T", "F"), "]"))
    OutputLine(Concat("--- f_es done ---"))
  ENDIF

  /* three spaces - the input a truthy language would call true */
  IF @b == "f_sp" THEN
    OutputLine(Concat("--- f_sp start ---"))
    OutputLine(Concat("F_SP=[", IIf(@sp, "T", "F"), "]"))
    OutputLine(Concat("--- f_sp done ---"))
  ENDIF

  /* the number zero */
  IF @b == "f_z" THEN
    OutputLine(Concat("--- f_z start ---"))
    OutputLine(Concat("F_Z=[", IIf(@z, "T", "F"), "]"))
    OutputLine(Concat("--- f_z done ---"))
  ENDIF

  /* the string "0" */
  IF @b == "f_z0" THEN
    OutputLine(Concat("--- f_z0 start ---"))
    OutputLine(Concat("F_Z0=[", IIf(@z0, "T", "F"), "]"))
    OutputLine(Concat("--- f_z0 done ---"))
  ENDIF

  /* the string "false" */
  IF @b == "f_sf" THEN
    OutputLine(Concat("--- f_sf start ---"))
    OutputLine(Concat("F_SF=[", IIf(@sf, "T", "F"), "]"))
    OutputLine(Concat("--- f_sf done ---"))
  ENDIF

  /* a genuine non-empty string as the condition */
  IF @b == "f_val" THEN
    OutputLine(Concat("--- f_val start ---"))
    OutputLine(Concat("F_VAL=[", IIf(@val, "T", "F"), "]"))
    OutputLine(Concat("--- f_val done ---"))
  ENDIF

  /* non-string branch values: a number and a date */
  IF @b == "f_type" THEN
    OutputLine(Concat("--- f_type start ---"))
    OutputLine(Concat("F_NUMBR=[", IIf(1 == 1, 42, 7), "]"))
    OutputLine(Concat("F_DATEBR=[", IIf(1 == 2, "no", Now()), "]"))
    OutputLine(Concat("--- f_type done ---"))
  ENDIF

  /* page-aborting call parked in the FALSE branch while TRUE is selected */
  IF @b == "f_lazyf" THEN
    OutputLine(Concat("--- f_lazyf start ---"))
    OutputLine(Concat("F_LAZYF=[", IIf(1 == 1, "T", RaiseError("iif-false-branch-evaluated")), "]"))
    OutputLine(Concat("--- f_lazyf done ---"))
  ENDIF

  /* the same probe in the other direction */
  IF @b == "f_lazyt" THEN
    OutputLine(Concat("--- f_lazyt start ---"))
    OutputLine(Concat("F_LAZYT=[", IIf(1 == 2, RaiseError("iif-true-branch-evaluated"), "F"), "]"))
    OutputLine(Concat("--- f_lazyt done ---"))
  ENDIF

  /* the control that makes the two branches above mean something: the same
     aborting call, invoked directly. This branch must NOT render - expect
     HTTP 422 with no start marker */
  IF @b == "f_ctrl" THEN
    OutputLine(Concat("--- f_ctrl start ---"))
    OutputLine(Concat("F_CTRL=[", RaiseError("iif-control-direct-call"), "]"))
    OutputLine(Concat("--- f_ctrl done ---"))
  ENDIF
]%%

Availability

Platform Available
Marketing Cloud Engagement Yes
Marketing Cloud Next Yes (since 67)

See also