DEV Community

Cover image for JavaScript Conditional Branching

JavaScript Conditional Branching

Bello Osagie on January 24, 2021

Conditions are boolean values , true or false. It is true if it meets the requirement, false otherwise. That is expressions (conditions) are...
Collapse
 
robertseidler profile image
RobertSeidler • Edited

I really like the ternary operator in theory. Beeing able to have branching in an expression is lovely, but I hate how hard it is to read in JS (exponentially worse for nested branches).
I think python chose a way better syntax for expression branching:
Instead of:

ageGroup = (age >= 18) ? 'Adult' : "Child"
Enter fullscreen mode Exit fullscreen mode

You got:

ageGroup = 'Adult' if age >= 18 else 'Child'
Enter fullscreen mode Exit fullscreen mode
Collapse
 
bello profile image
Bello Osagie
a = 5
b = 10
check_condition = True if a < b else False 
print(check_condition) # True
Enter fullscreen mode Exit fullscreen mode

That may be true because some people will disagree with you. But generally, python has a better syntax. That is why Python is used for complex code structure in AI, machine Learning, data science, etc.

If you already know JavaScript on the browser or web, I recommend trying out Brython...

Collapse
 
robertseidler profile image
RobertSeidler

I'll give Brython a go, never heard of it :D

But JS has it's pros, too. In python I do miss having functions as first class objects (although that might help python, still. Making it more clear). And everything async feels slugish in python compared to js.

I enjoy switching back and forth every now and again.

Thread Thread
 
bello profile image
Bello Osagie

That's because you used Python... It happens to everyone at some point...

Collapse
 
peerreynders profile image
peerreynders
Collapse
 
robertseidler profile image
RobertSeidler

I do feel more and more drawn towards a more functional / declarative style of programming. It makes it (at least for me) more intuitive (both when reading and writing), than either imperative or object oriented.

Thread Thread
 
peerreynders profile image
peerreynders

I'm not sure that Python is the right "medium" for a functional/declarative style.

Collapse
 
rxliuli profile image
rxliuli

In more complex situations, it would be more appropriate to use object-oriented polymorphism (class support contains state rather than just functions)

Collapse
 
peerreynders profile image
peerreynders

ECMAScript Pattern Matching (if it ever lands) can clean up complex expression based data transformations considerably.

Collapse
 
robertseidler profile image
RobertSeidler

pattern matching would be awesome. I wanna have it everywhere

Thread Thread
 
peerreynders profile image
peerreynders • Edited

Sometimes the stars align and you get to "fake it" with a Bitwise OR (|):

const KIND = Object.freeze({
  Equilateral: 'equilateral',
  Isosceles: 'isosceles',
  Scalene: 'scalene',
});

const LEGS = Object.freeze({
  NotEqual: 0x0,
  LargerEqual: 0x1,
  SmallerEqual: 0x2,
});

const keepPositive = (side) => side > 0;
const numericAsc = (a, b) => a - b;

function kind(sides) {
  const triangle = sides.filter(keepPositive);
  if (triangle.length != 3)
    throw new Error(`Not all sides are positive: ${sides}`);

  triangle.sort(numericAsc);
  const [a, b, c] = triangle;
  if (a + b <= c) throw new Error(`Triangle inequality violated: ${sides}`);

  // "faking" pattern matching
  switch (
    (a === b ? LEGS.SmallerEqual : LEGS.NotEqual) |
    (b === c ? LEGS.LargerEqual : LEGS.NotEqual)
  ) {
    case LEGS.SmallerEqual | LEGS.LargerEqual:
      return KIND.Equilateral;
    case LEGS.SmallerEqual:
    case LEGS.LargerEqual:
      return KIND.Isosceles;
    default:
      return KIND.Scalene;
  }
}

console.assert(kind([2, 2, 2]) === 'equilateral', 'Equilateral failed');
console.assert(kind([3, 4, 4]) === 'isosceles', 'Isosceles (first side) failed');
console.assert(kind([4, 3, 4]) === 'isosceles', 'Isosceles (second side) failed');
console.assert(kind([4, 4, 3]) === 'isosceles', 'Isosceles (third side) failed');
console.assert(kind([3, 4, 5]) === 'scalene', 'Scalene failed');
console.assert(kind([0.4, 0.6, 0.3]) === 'scalene', 'Short sides scale failed');
console.assert(throws(() => kind([0, 0, 0])), 'No size failed to throw');
console.assert(throws(() => kind([3, 4, -5])), 'Negative side failed to throw');
console.assert(throws(() => kind([7, 3, 2])), 'Triangle inequality violation failed to throw');
console.log('done.');

function throws(fn) {
  let caught = false;

  try {
    fn();
  } catch (_error) {
    caught = true;
  }
  return caught;
}
Enter fullscreen mode Exit fullscreen mode
Thread Thread
 
robertseidler profile image
RobertSeidler

clever, I had to stare at it for a few minutes to go through all the possibilities.

That's where the real beauty of a neatly syntaxed pattern matching expression could do a great deal to make it super obvious, what one wants to express with their code.

Thread Thread
 
peerreynders profile image
peerreynders

I had to stare at it for a few minutes to go through all the possibilities.

That's possibly because of lack of familiarity.

The Rust equivalent doesn't look that different to me:

fn categorize(mut sides: [T; 3]) -> Option<Category> {
    sides.sort_unstable_by(|a, b| a.partial_cmp(b).unwrap());
    let [a, b, c] = sides;
    let is_triangle = a + b > c;
    if is_triangle {
        Some(match (a.eq(&b), b.eq(&c)) {
            (true, true) => Equilateral,
            (true, _) | (_, true) => Isoceles,
            _ => Scalene,
        })
    } else {
        None
    }
}
Enter fullscreen mode Exit fullscreen mode

in particular

match (a.eq(&b), b.eq(&c)) {
    (true, true) => Equilateral,
    (true, _) | (_, true) => Isoceles,
     _ => Scalene,
}
Enter fullscreen mode Exit fullscreen mode

In Erlang (and Elixir is similar) something like (again, assuming that the sides are sorted by length so that equal sides will always be adjacent)

case Sides of
  {A, A, A} -> "equilateral";
  {A, A, C} -> "isosceles"; 
  {A, B, B} -> "isosceles";
  _ -> "scalene"   
end.

Enter fullscreen mode Exit fullscreen mode

or

case Sides of
  {A, B, C} when A == B andalso B == C -> "equilateral";
  {A, B, C} when A == B orelse B == C -> "isosceles"; 
  _  -> "scalene"
end.

Enter fullscreen mode Exit fullscreen mode

ReScript (aka ReasonML; JavaScript flavored OCaml)

let kind = switch (a === b, b === c) {
| (true, true) => "equilateral"
| (true, _) | (_ , true) => "isosceles"
| _  => "scalene"
}
Enter fullscreen mode Exit fullscreen mode

etc.

Thread Thread
 
robertseidler profile image
RobertSeidler • Edited

That's possibly because of lack of familiarity.

very likely.

And nice overview, of how different languages handle the same problem