Home > Software design >  How to check whether all number elements in an array are even numbers with recursion?
How to check whether all number elements in an array are even numbers with recursion?

Time:09-17

I'm currently learning as much as I can about recursion but I'm really stuck on how to use it to check whether all the number elements in an array are even numbers. With ES6, I can use "every" to achieve the desire outcome as follow:

const isEven = (arr) => arr.every((item) => item % 2 === 0);
console.log(isEven([2,4,6,8])); // true
console.log(isEven([2,4,6,9])); // false

The following is my attempt as recursion:

const isEven = (arr) => (arr.length === 0) ? true : isEven(arr.slice(1)) % 2 === 0;
console.log(isEven([2,4,6,8])); // true
console.log(isEven([2,4,6,9])); // true

As you can see, the result is incorrect for the 2nd example and I have a feeling that I'm not on the right path either. Can someone kindly show me what I'm doing wrong?

CodePudding user response:

The problem with your implementation is that isEven returns a boolean but you try to use the result as if it is a number. So, it turns out that the return result is solely based on the number of elements, as each time it gets flipped:

console.log("true % 2 === 0 :", true % 2 === 0);
console.log("false % 2 === 0:", false % 2 === 0);

const isEven = (arr) => (arr.length === 0) ? true : isEven(arr.slice(1)) % 2 === 0;
console.log(isEven([]));     // true
console.log(isEven([1]));    // false
console.log(isEven([1, 1])); // true

If we write out your function fully instead as a single conditional operator, we can see what is happening better by adding some logging:

const isEven = (arr) => {
  if (arr.length === 0) {
    console.log("base case - finish recursion. result: true");
    return true;
  }
  const recursionValue = isEven(arr.slice(1));
  const result = recursionValue % 2 === 0;
  console.log(`After recursion: 
  recursionValue: ${recursionValue} 
  result        : ${result}`);
  return result;
}

console.log(isEven([2,4,6,9])); // true
.as-console-wrapper { max-height: 100% !important; }

Or to illustrate this differently, this is how the recursion will be resolved:

isEven([2,4,6,9])
| --> | isEven([4,6,9]) % 2 === 0
|     | --> | isEven([6,9]) % 2 === 0
|     |     | --> | isEven([9]) % 2 === 0
|     |     |     | --> isEven([]) % 2 === 0
|     |     |     |   | --> arr.length === 0 --> true
|     |     |     |   | true % 2 === 0 --> false
|     |     |     | false % 2 === 0  --> true
|     |     | true % 2 === 0 --> false
|     | false % 2 === 0 --> true
| true

To achieve what you want, you need to be checking the first element each time, not the result of isEven.

  • If the array is empty, then the whole array must have been even. This is the base case. The recursion ends and the result is true
  • If the first element is odd, then not all of the array is even. This is a terminal condition. End the recursion and return false - that means that the base case will not be reached.
  • If there are items in the array and the first item is even, just recurse by using the rest of the array checking the above two conditions each time.

This ensures that you either get true if the second condition is never reached, or false if it ever is.

const isEven = (arr) => {
  if (arr.length === 0) // empty - base case
    return true;
  if (arr[0] % 2 === 1) // odd - stop and return false
    return false;
    
  return isEven(arr.slice(1)); // recursion
}

console.log(isEven([2,4,6,8])); // true
console.log(isEven([2,4,6,9])); // false

CodePudding user response:

The problem is in the isEven(arr.slice(1)) % 2 === 0 part. The isEven function is supposed to return a boolean, for which it doesn't make sense to to take the modulo.

Instead, you can do something like this:

isEven = arr => arr.length === 0 ? true : arr[0] % 2 === 0 && isEven(arr.slice(1));

CodePudding user response:

The premise should be:

The array is even if the first element is even and the rest of the array (which is also an array) is also even

So basically:

const isEven = (arr) => (arr.length === 0) ? true : arr[0] % 2 === 0 && isEven(arr.slice(1));

Which can be rewritten a little bit with destructuring to exemplify it better.

const isEven = ([first, ...rest]) =>
    (first % 2 === 0) &&
    (rest.length ? isEven(rest) : true);
  • Related