Oftentimes, when writing software, we need to realize processes like this:

“If A, then do B, otherwise do C.”,

or that:

“If A, then set X to B, otherwise set X to C.”.

And there are also the more complex ones:

“If A is equal to B, do X. If A is equal to C, do Y. (And more…)”.

Essentially, this is mapping some input or condition to either some data to use, or some action to perform.

There are different ways to implement these, and while some implementations are use-case-specific, there are ways that are easier to write, read, maintain and reason about, and ways that are not.

In this post, I’d like to share and discuss simple examples for the most relevant and frequently used implementations. While most of the code examples are given in PHP, the equivalent in JavaScript almost always looks exactly like the PHP code.

Using an if Statement

Every imperative programming language—actually a lot of other language types, too—contains a language construct that allows for conditional branching, meaning you can realize “If this, then that.”, and similar. Yes, I am talking about the if keyword we all know from both PHP and Javascript. And also else, or even else if—or elsif or elif etc.

Let’s start with a simple binary condition:

if ( $a === $b ) {
	doX();
} else {
	doY();
}

According to our condition, we either perform this action, or that action.

Of course, we can not only execute a single function. We can execute multiple, or set one or more values, or do both. Here is an example where we set a variable according to a binary condition:

if ( $a ) {
	$x = $b;
} else {
	$x = $c;
}

It’s also possible to either nest conditionals, and/or create a more complex conditional setup, with more than just two branches. For example, like so:

if ( $a === $b ) {
	doX();
} elseif ( $a === $c ) {
	doY();
} else {
	doZ();
}

As long as it’s simple like this, it’s all good. However, most of the time, we don’t have such simple code blocks. Instead, we might have some non-conditional code first, then some conditional code, maybe nested, maybe introducing more than just one additional branch/path, some more non-conditional code, … And eventually, we easily end up with something like the following, now in pseudocode—to allow for less boilerplate and easier comparison between code and the flow chart further down.

Some code here...

if ( Some condition? ) {
  Do X.
} else {
  if ( ! Some other condition? ) {
    Do ABC.
  }
  Do Y.
}

Some more code here...

if ( Yet another condition? ) {
  Do M.
} else if ( Final condition? ) {
  Do T.
} else {
  // Bail!
  return 42;
}

More code...

Here’s a visual representation of the above code, in the form of a flow chart:

Flow chart for pseudocode example.

This code example is not really complex. It doesn’t contain any loops, no goto statements, no recursion etc. It’s just one nested if inside an ifelse, and one subsequent ifelseifelse. But still we end up with a total of nine different code paths from start to finish.

Personally, I think that there is no actual need for else. Conditionally doing one particular thing (or code block), yes or no, that’s all good. But doing one thing or another, as part of some larger program flow, this can be implemented differently, allowing for easier understanding and maintenance. You can easily realize this by using return inside the if. The part that you would have put in the else then just lives in the same scope that the if block lives in, which is the function scope, most of the time. Sometimes you have to move a more complex if statement to a separate function, if you have multiple results of the compound conditional.

Using a Ternary Expression

If you want to set some variable to one of two possible values, according to some condition, you do not have to reach for a multi-line if statement. Instead, you could make use of a ternary expression:

$x = $a ? $b : $c;

Usually, this fits in a single line, and, in this case, it also means that it is easier to read and understand. Also, by using a ternary when setting the value for some variable, it is much clearer what you actually do. There is a condition, and one of two things happens. But these are not unrelated things, it is two times setting a (different) value for the same variable. Doing this by using a ternary does not create two branches in the program flow.

Personally, I find using a ternary much better than this, which was our second code example:

if ( $a ) {
	$x = $b;
} else {
	$x = $c;
}

And even more so if you are writing JavaScript code! When using block-scope variable declaration, using let and const, you are forced to use let in this case. Even if the value, once set, never changes:

// Using if–else:
let x;
if ( a ) {
	x = b;
} else {
	x = c;
}


// Using ternary:
const x = a ? b : c;

Yes, the very first code example in this section, the one-liner, looks really simple. But it doesn’t have to be a single variable that you check. You can also incorporate a more complex (or rather: longer) conditional expression. In that case, I like to wrap it onto multiple lines, like so:

$x = ( $a && in_array( $a, $foo, true ) )
	? $b
	: $c;

The parentheses around the conditional are not necessary, but I usually like to write it like so for composite conditionals, made up of two or more parts, and even more so if I need to wrap the conditional onto multiple lines.

Of course, you can not only use a ternary expression when setting a value. You can use it just as well when returning one of two values:

return $a ? $b : $c;

While it’s possible to nest ternaries, I strongly advise against doing that. If you find yourself reaching for this, for example, because of your particular conditional setup, I think you are much better off using a dedicated function/method to return the respective value, according to some passed or otherwise available data.

Using a switch Statement

If you need to cater for more than just two possible states or situations, you could reach for an if expression with however many elseif you might need before the final else.

I find that not super readable, and would use a switch statement instead:

switch ( $a ) {
	case $b:
		doX();
		break;

	case $c:
		doY();
		break;

	default:
		doZ();
}

This is like the natural technique when you need to handle a limited number of known states, or types, or configurations. You would see switch statements in structures or patterns such as maps, reducers, factories, or facades.

However, you can use a switch statement not only to compare some specific variable to a number of predefined values. Most programming languages—including both JavaScript and PHP—understand an arbitrary expression passed to switch, and also evaluate it at runtime. This means that you can use switch for use cases like the following, maybe rather unusual, one:

switch ( true ) {
	case $a === $b:
		doX();
		break;

	case $b > $c:
		doY();
		break;

	default:
		doZ();
}

This is perfectly valid syntax, and it is interpreted as you would assume. The first case that matches the result of the passed expression, gets executed, and no other one (assuming break in all cases).

One small gotcha here is that some programming languages, for example, PHP, use loose comparison, meaning ==. JavaScript, on the other hand, performs strict comparison, ===.

Using a Map

If your use case is to “convert” some distinct, known value to some other distinct, known value, you are actually doing what is known as mapping in mathematics. Mapping means for each value from the source domain, you get some other value from the target domain. Depending on your needs, it may be that multiple source values are mapped to the same target value, or that you get one of many possible target values for a given source value. In that case, this is dynamic mapping, and not the much more widely used static mapping.

The data structure for a map can be as simple as an associative array or a stdClass object in PHP, or an object literal in JavaScript. Let’s take the publication checklist as an example. A map of the default status icons for a publication checklist item, indexed by the different status values, could look like so:

$icons = [
	'COMPLETED'  => '✔',
	'INCOMPLETE' => '✘',
	'INFO'       => 'ℹ',
	'REQUIRED'   => '!',
];

Yes, the array keys would better be class constants such as Item::COMPLETED instead of hard-coded values, but that’s for another post. 😉

Using the map, and thus performing the actual mapping, could be as easy is this:

$icon = $icons[ $status ] ?? '';

It should be obvious that this is much more efficient than a switch statement with a handful of cases, or an ifelseif construct covering just as many possible results for the conditional. While the overall amount of operations for both switch and if depends on where the matching value/expression is, using a map will only ever be a single operation.

I am talking high level here, not memory lookup, default value etc. The above map implementation is a single time:

“Is there a target value defined for this source value?”.

If there is, we already have the result. If not, we provide the default right away.

Using switch or if means:

“Does the given expression match this first value? The second? The third? …”,

until a matching value has been found.

Of course, you can not only use a map for scalar values. You can also map some input to an object/instance, or an array with configuration data, or a callback, as shown here:

$callbacks = [
	'foo' => 'doX',
	'bar' => [ $this, 'doY' ],
];

Oftentimes, a map is also used to convert some publicly available value into some more internal structure, for example, mapping some type (or interface) to a fully-qualified class name (FQCN). This would also allow for aliasing or backward compatibility, by mapping two or more types to the same FQCN:

$classes = [
	'foo' => Foo::class,
	'bar' => Bar::class,
	// Backward compatibility for deprecated type:
	'fuu' => Foo::class,
];

Abstracting Implementation Details

All of the above can be placed anywhere in your codebase. It could live amongst a lot of other code in a gigantic function. It could live in a separate method in the one class where you need the logic. But the best way, and this is from my personal experience, is to put this mapping logic in a separate and dedicated function.

Advantages of this include:

  • You can easily unit-test just the mapping logic, because it exists as a separate and publicly available unit.
  • You can easily and efficiently reuse the mapping wherever you need, which makes your codebase more DRY, and the mapping consistent.
  • You can safely refactor the mapping logic, because it is an implementation detail encapsulated in the function.

You may have heard statements such as “Every function should do just one thing.”, and if you think about what all of the code examples are about, you might realize that it is this: determine what value to use, or action to perform. And this is one “thing”, so it should live in a separate function.

Of course, a single ternary expression, or also an if statement without any else part can be placed somewhere inline. I am talking about more complex setups, where you either have more than a binary conditional, and/or it is not about a single line (e.g., value or action) that gets executed.

This means that every switch statement ideally lives in a separate function, and there is no real reason to have any code after the switch, because you can return in the case blocks.

Thoughts?

What is it that you reach for, when you need to realize some multi-conditional logic?

How do you feel about using else? Or elseif?

Do you usually abstract that kind of logic into a separate function, or do you place it right where you need (the result of) it?

Leave a Reply

Your email address will not be published. Required fields are marked *