Fundamental exercise to check divisibility of a number

Viewed 112

I got stuck on this exercise. I don't understand why they need to have the first condition. Can I ignore that part?

The exercise is from https://www.w3resource.com/javascript-exercises/javascript-basic-exercise-46.php:

Write a JavaScript program to check two given non-negative integers that whether one of the number (not both) is multiple of 7 or 11.

function valCheck(a, b) {
  if (!((a % 7 == 0 || a % 11 == 0) && (b % 7 == 0 || b % 11 == 0))) {
    return ((a % 7 == 0 || a % 11 == 0) || (b % 7 == 0 || b % 11 == 0));
  } else
    return false;
}

console.log(valCheck(14, 21));
console.log(valCheck(14, 20));
console.log(valCheck(16, 20));

5 Answers

TL;DR – No, it's not redundant!


Write a JavaScript program to check whether exactly one of the two given non-negative integer numbers is a multiple of 7 or 11.

To explain why it's not redundant, let's break this solution function down.

function valCheck(a, b) {
  if (!((a % 7 == 0 || a % 11 == 0) && (b % 7 == 0 || b % 11 == 0))) {
    return ((a % 7 == 0 || a % 11 == 0) || (b % 7 == 0 || b % 11 == 0));
  } else
    return false;
}

First Conditional

The first condition is !((a % 7 == 0 || a % 11 == 0) && (b % 7 == 0 || b % 11 == 0)). The x % n == 0 expressions are checking whether the given x is divisible by (a multiple of) either 7 or 11, but the expression is written in an overly verbose way that makes it a little difficult to understand.

Using De Morgan's law, we can rewrite it:

!((a % 7 == 0 || a % 11 == 0) && (b % 7 == 0 || b % 11 == 0))

⇔

!(a % 7 == 0 || a % 11 == 0) || !(b % 7 == 0 || b % 11 == 0)

⇔

(a % 7 != 0 && a % 11 != 0) || (b % 7 != 0 && b % 11 != 0)

Now, we can tell that this condition is checking for the case where either a or b isn't divisible by 7 nor 11.

When this condition is true, we return the result of a second condition. If it is false, then we know neither input is a multiple of 7 or 11, so we return false outright.

Second Conditional

The second condition is:

(a % 7 == 0 || a % 11 == 0) || (b % 7 == 0 || b % 11 == 0)

This one is much less opaque than the first, and checks whether any of the 4 sub-conditions are true (the parenthesis are irrelevant here, since they're all "or" || operators).

Since we already know from the first condition that at least one of the inputs isn't a multiple of 7 or 11, we can now simply return whether at least of the inputs is. Put another way, if we know that this second condition is true, then we can be certain that exactly one of the numbers is a multiple of 7 or 11.

Are the Conditions Redundant?

So to answer your question, no, the first conditional isn't redundant, since it checks for whether at least one of the inputs isn't a multiple or 7 or 11. We need both of the conditional expressions to properly check that exactly one of the inputs is a proper multiple or 7 or 11.

If the "but not both" clause wasn't there, then we could use just the second conditional expression, but since it is, we need both pieces of information.


Wanted to note, I lied a little in saying we need both expressions, since it is indeed possible to solve this exercise with a single expression!

function valCheck(a, b) {
  return (a % 7 == 0 || a % 11 == 0) != (b % 7 == 0 || b % 11 == 0);
}

console.log(valCheck(14, 21)); // false
console.log(valCheck(14, 20)); // true
console.log(valCheck(16, 20)); // false

I'll leave it to the reader to figure out why this works!

There are many repeating functions, let's refactor for clarity:

function isMultipleOf7Or11(num) {
  return num % 7 == 0 || num % 11 == 0;
}

function valCheck(a, b) {
  var isAVarMultiple = isMultipleOf7Or11(a);
  var isBVarMultiple = isMultipleOf7Or11(b);
  
  if(!(isAVarMultiple && isBVarMultiple) {
      return isAVarMultiple || isBVarMultiple;
    else 
      return false; 
  }
}

Now to simplify the condition:

!(isAVarMultiple && isBVarMultiple) is also equal to !isAVarMultiple || !isBVarMultiple according to De Morgan's negation of a conjuction, which returns true if at least one of them is not a multiple of 7 or 11

and in the return value the condition isAVarMultiple || isBVarMultiple returns true if at least one of them is a multiple of 7 or 11

We can conclude that it is a XOR operation on two values, which would return true only if one and only one of the variables is a multiple of 7 or 11

Outputs:

console.log(valCheck(14, 21));

returns false

console.log(valCheck(14, 20)); 

returns true

console.log(valCheck(16, 20));

returns false

I recommend you look at the code by the debugger, not for this example only, but when you will be stuck in difficult situations, you can split your code in a small and readable pieces and see what is happening in there.

For example this can be transformed to this

Not Sure If I can make this more readable :)

function valCheck (a, b) {

  debugger;

  const canADividedTo7 = a % 7 == 0;
  const canADividedTo11 = a % 11 == 0;

  const canADividedTo7Or11 = canADividedTo7 || canADividedTo11;

  const canBDividedTo7 = b % 7 == 0;
  const canBDividedTo11 = b % 11 == 0;

  const canBDividedTo7Or11 = canBDividedTo7 || canBDividedTo11;

  // true when both both side of expression are true
  const canAAndBDividedTo7Or11 = canADividedTo7Or11 && canBDividedTo7Or11;

  // true when at least one of the expression is true
  const canAOrBDividedTo7Or11 = canADividedTo7Or11 || canBDividedTo7Or11;
  //
  if (!canAAndBDividedTo7Or11) {
 
     return canAOrBDividedTo7Or11;

  } else {

     return false;

  }

}

console.log(valCheck(14, 21));

console.log(valCheck(14, 20));

console.log(valCheck(16, 20));

Who would write anything that inane and complicated?

Let's take this mess:

function valCheck(a, b) {
  if (!((a % 7 == 0 || a % 11 == 0) && (b % 7 == 0 || b % 11 == 0))) {
    return ((a % 7 == 0 || a % 11 == 0) || (b % 7 == 0 || b % 11 == 0));
  } else
    return false;
}

and refactor it into something readable:

function valChec( a, b ) {
  const aIsDivisible = a % 7 === 0 || a % 11 === 0 ;
  const bIsDivisible = b % 7 === 0 || b % 11 === 0 ;

  return aIsDivisible ? !bIsDivisible : bIsDivisible;
}

You could even make it a one-liner with a ternary expression (at the expense of a few more division ops... but given the original code, that's not an issue):

const isMagic = x => x % 7 === 0 || x % 11 === 0;
const valCheck = (a,b) => isMagic(a) ? !isMagic(b) : isMagic(b);

Yes, you're right in finding the answer to that question redundant; also, I believe the ! is incorrect and makes the code always return false.

A more reasonable solution to the problem would be to just return the boolean expression:

function valCheck(a, b) {
  return ((a % 7 == 0 || a % 11 == 0) || (b % 7 == 0 || b % 11 == 0));
}

Alternatively, if you really wanted to use an if/else construct, it would be better to just return true in the first case:

function valCheck(a, b) {
  if ((a % 7 == 0 || a % 11 == 0) && (b % 7 == 0 || b % 11 == 0))
    return true;
  else
    return false;
}

Hope this helps with your understanding of JavaScript! But as suggested in the comments, you might want to learn JavaScript from other sources.

Related