user2693928
user2693928

Reputation:

Remove if statement

If if statement could be avoided it is considered a good practice.

For example this code:

if (a > 80) {
  a = 80;
}

Can become this:

a = Math.min(80, a);

That way the code is considered cleaner because there is no branch logic.

But is there any way to avoid if for more complex problems like this:

if (array.length > 5) {
   array = array.reverse().join('');
} else {
   array = 'array is lte 5';
}

If array length is > 5 then reverse it and join it, otherwise return "array is lte 5".

This is simple example but more complex than the first example and it's hard to remove if.

How mathematics handle branches and is it possible to express this logic in mathematics.

I can extract it to a separate method but it will only move the if statement in the method itself, it will not remove it.

I can imagine I can use some functions from Ramdajs but i didn't find appropriate one and even if I find one the if will be there i guess - it will be only abstracted.

Also imagine this sudo code:

if (file_exists(file)) {
   content = file_read(file);
   if (content.startsWith('config')) {
       ret = 'config:'; 
   } else if (content.endsWith(':app')) {
       ret = ':app';
   }  
} else {  
   ret = '';
}

This code has only 2 if statements but already is a nightmare to read and change.

Is it possible to use mathematic and/or express it more clearly avoiding branches.

I know in mathematics there is no "read file" but it was just an example.

Thank you

Upvotes: 1

Views: 1969

Answers (2)

customcommander
customcommander

Reputation: 18901

One approach would be to put the thing you need to operate on in a "box" on which you apply a series of operations (i.e. functions). This forces you to remove any nested conditions.

This pseudo-code:

if (file_exists(file)) {
   content = file_read(file);
   if (content.startsWith('config')) {
       ret = 'config:'; 
   } else if (content.endsWith(':app')) {
       ret = ':app';
   }  
} else {  
   ret = '';
}

could be replaced with:

const ret =
  [file]
    .map(x => file_exists(x) ? file_read(x) : '')
    .map(x => x.startsWith('config') ? 'config:' : x)
    .map(x => x.endsWith(':app') ? ':app' : x)
    .pop();

Note that the above could we converted using function composition:

const ret =
  pipe(
    ifElse(file_exists, file_read, always('')),
    when(startsWith('config'), always('config:')),
    when(endsWith(':app'), always(':app')))
      (file)

Of course one could argue that you execute unnecessary checks but unless a performance issue has been identified, I'd always favour readability over anything else.

Can we improve readability here? We certainly can try:

const ret =
  [file]
    .map(load_file_content)
    .map(when_starts_with('config'))
    .map(when_ends_with(':app'))
    .pop();

Or

const ret =
  pipe(
    load_file_content,
    when_starts_with('config'),
    when_ends_with(':app'))
      (file)

I find this readable but others may not so 🤷‍♂️

Upvotes: 2

Siguza
Siguza

Reputation: 23850

Besides the ternary operator (which probably isn't gonna make things more legible), have you considered early returns?

if (!file_exists(file)) {
    return '';
}

content = file_read(file);
if (content.startsWith('config')) {
    return 'config:'; 
}
if (content.endsWith(':app')) {
    return ':app';
}

return ...;

There's still gonna be just as much branching logic behind the scenes, but this way you can logically unentangle semantically different code blocks from each other.

Upvotes: 1

Related Questions