Got on codewars js challenge

I got stuck in codewars challenge that is called +1 Array.
The challenge says:
image

I have only 2 failed test cases:

This is a link to the challenge:

My code so far!:

function upArray(arr) {
  const checkIfnegative = (element) => element <= -1;
  const checkIfSingleDigit = (element) => element > 9;
  if (arr.some(checkIfnegative)) {
    return null;
  } else if (arr.some(checkIfSingleDigit)) {
    return null;
  }
  arr = arr
    .map((num) => {
      return num.toString();
    })
    .join("");
  let num1;

  if (arr == []) {
    return null;
  } else {
    num1 = parseFloat(arr) + 1;
    num1 = num1
      .toString()
      .split("")
      .map(function (i) {
        return parseFloat(i);
      });
  }
  return num1;
}

So what are my mistakes and what I need to fix on my code?

this doesn’t make sense, you have changes it to a string just above so you can’t compare to array, also [] == [] is false

I will get back later for more debugging, but for now I would suggest to make each step with a variable instead of chaining and see if at each step you have the expected value

I’m going to point on some things that can be improved, but my critques will be point by point and not holistic so improving upon them may not make your code pass necessarily, but I will probably look at it more holistically in a second comment.

//function upArray(arr) {
/*
This entire block could be done in one loop with one if statemnet

  const checkIfnegative = (element) => element <= -1;
  const checkIfSingleDigit = (element) => element > 9;
  if (arr.some(checkIfnegative)) {
    return null;
  } else if (arr.some(checkIfSingleDigit)) {
    return null;


  } 
*/

/*
this block would have the same result as: arr = arr.join("")

  arr = arr
    .map((num) => {
      return num.toString();
    })
    .join("");
  
*/

/*
  let num1;

variables can point towards the same object, but an anonymous object will always be unique so nothing could ever be the same as it, and I would also recommend using strict equality almost always

what you mean to check is arr.length, and you also reassigned your param so you no longer have a refernce to the orignal array (that may not be a problem I am simply pointing it out)

checking the length of the array would probably best done at the top

  if (arr == []) {
    return null;
  } else {
    
    parseFloat probably is not the best choice for number coercion here
    num1 = parseFloat(arr) + 1;
    num1 = num1
      .toString()
      .split("")
      .map(function (i) {
        return parseFloat(i);
      });
  }
  return num1;
}
*/

The primary reason your code is failing is because you are attempting to make the array elements into one number, but some of the test have arrays that exceed Number.MAX_SAFE_INTEGER so when you attempt do math on them it just does it wrong. You will need to do some refactoring to get your code to pass, and if you would like I will tell you the logic behind my approach which did pass the test.

Hi, the interesting thing about any one data type, e.g. int, float, etc, is that they have a size cap. You’ve probably have heard of those limits such as the 32-bit int, or 64-bit int, etc. This actually means there’s an upper/lower limit to what a int can be.

In every language typically there will be a constant specifically storing the max/min value an integer can be, in Javascript it is MAX_SAFE_INTEGER and MIN_SAFE_INTEGER with 2^53-1 and 2(2^53-1) as values respectively.

But you probably realize we type large numbers here no problem, nor do we have any trouble multiplying/adding these large numbers beyond the storage limit comfortably with any calculator.

This is because large numbers can be stored as an array of n size, with n being the number of digits in the number.

Therefore your logic should not be to join the array together and work on the resulting number as that can result in int overflow, but you should work on the array itself.

Cases to consider:
what happens when you add the 1 and that digit becomes larger than 9?
And
What happens when that digit that becomes larger than 9 is the leading digit? (array element indexed at 0?)

good luck.

This code below I presume would pass there test, but the challenge is on a version of node that does not accept BigInt which is also why I am just showing the code out right as it cannot be used. It is not dissimilar to the idea behind your approach, and is exactly how I attempted the challenge the first time, of course it failed then because of how big the numbers were. I did pass the test in way more similar to what @MatchaCrisp is describing .

function upArray(arr){
    if (arr.length === 0) return null;
    let sum = 0n;
    let sumArr = [];
  
    for (let i = 0; i < arr.length; i++) {
        if (arr[i] < 0 || arr[i] > 9) return null;        

        sum += BigInt(arr[i]) * (10n ** BigInt(arr.length - (i + 1)))
    }
    
    sum++;
    
    while (sum) {
        sumArr.push(Number(sum % 10n));
        sum /= 10n;
    }
    return sumArr.reverse();
}

This method is sure to be less efficient than not constructing a number at all, but it is more intuitive.

There are some lines of your code that I don’t understand.
Could you explain them to me?
@caryaharper

what do you not understand of that code?

@ilenia
these lines of code that I mentioned it.
Such as : 0n what n does with the number in the code?

  • the BigInt in the

and what the while loop does do in the code?

BigInt is a type of data for integers too big to be precisely saved in the usual number format, they are identified with an n after the number, like 10000000000n

the while loop is using the truthiness/falsyness of numbers, until sum is a number different from 0 the loop keeps going, when instead it becomes 0n it is falsy and the loop stops going

What =/ does do in sum /= 10n; ?

sum /= 10n is shorthand for sum = sum / 10n, it is dividing by 10 the value of sum and storing it again in sum

and What are these some refactoring ?

refactoring means to adjust code to fit environment, comment for clarity, remove redundant code etc etc. Basically it’s code cleanup/minor optimization. In this specific case tho as @caryaharper already stated:

…the challenge is on a version of node that does not accept BigInt…

So even if you refactor your original logic to use BigInt which would possibly encompass all testcases codewars environment may not accept it as correct.

And even if that logic does pass, it is inherently flawed due to anyone being able to add as many more elements to the array as they like and eventually exceed even the bigInt max limit.

You are right.
I enter @caryaharper 's solution but it did not pass the challenge !!

function upArray(arr){
    //checks to see if the array is empty
    if (arr.length === 0) return null;

    //Initializes the BigInt form of 0
    let sum = 0n;

    //Initializes an array that will become the return value
    let sumArr = [];
  
    //loops through the elements of arr
    for (let i = 0; i < arr.length; i++) {
        //checks for invalid values
        if (arr[i] < 0 || arr[i] > 9) return null;        

        //creates a number from the array elements
        //the math at the end gives the numbers place
        sum += BigInt(arr[i]) * (10n ** BigInt(arr.length - (i + 1)))
    }
    
    //adds 1 to sum
    sum++;
    

    //loops while sum is not falsey, or in this case 0
    while (sum) {
        //grabs the lowest number off of sum and pushes it into sumArr
        //it also turns said BigInt back into a number
        sumArr.push(Number(sum % 10n));

        //removes the lowest number off of sum
        //for a normal number you will want to use Math.floor
        //BigInt does not have floats
        sum /= 10n;
    }
    //returns sumArr reversed as the method I used created the array backwards
    //I could have used unshift, but I assumed it would have taken longer 
    return sumArr.reverse();
}

@ abdulrahman.mhd.anas

I dont think anyone on this thread has yet suggested a passing solution, Im in a similar boat as you I wrote this:

function upArray(arr){
  const hasNegative = arr.find(v => v < 0);
  const greater= arr.find(v=> v>9)
  const unexplainedTestCase=(arr)=>{
    return (arr.length===1&&arr[0]===1)?true:false
  }
  if(hasNegative||greater){return null}
  const callback = x => Number(x);
  arr= Array.from(Number(arr.join(''))+1+'',callback)
  return unexplainedTestCase(arr)?null:arr
}

have you tried doing something like if its over a certain length than splitting the array into smaller parts and adding the 1 to the end piece…

but I think the challenge wants us to do something else… i.e. we are going about it the wrong way

here is a working solution similar to what i described, it works to pass all the tests, but …its still not good:

function upArray(arr){
  const hasNegative = arr.find(v => v < 0);
  const greater= arr.find(v=> v>9)
  const unexplainedTestCase=(arr)=>{
    return (arr.length===1&&arr[0]===1)?true:false
  }
  if(hasNegative||greater){return null}
  if(arr.length>Number.MAX_SAFE_INTEGER.toString().length){
    arr[arr.length-1]=arr[arr.length-1]+1
    return arr
  }
  const callback = x => Number(x);
  arr= Array.from(Number(arr.join(''))+1+'',callback)
  return unexplainedTestCase(arr)?null:arr
}

there is a better way to solve for this…

I have refrained from posting a solution outright and instead mentioned the correct logic, as I believe that the algorithm alone would be sufficient in coming up with the correct code, and that process would be good for learning.

More specifically,to solve this you are to add 1 to the array from the end to the beginning, taking into consideration the following two cases:

  1. When the element becomes larger than 9 after adding 1
  2. when that element that is larger than 9 is also the element at index 0.

Here is working code of the correct algorithm in action:

function upArray(arr){
  if (arr.length===0) return null;
  let carry=1;
  for (let i = arr.length-1; i >= 0; i--) {
    if (arr[i]>9 || arr[i]<0) return null;
    if (carry>0) {
      arr[i]+=carry;
      carry=Math.floor(arr[i]/10);
      arr[i]=arr[i]%10;
    }
  }
  if (carry>0) arr.unshift(carry);
  return arr;
  
}
2 Likes

My actual solution to the challenge looks very similar to @MatchaCrisp although mine certainly has some redundancy in it

const upArray = arr => {
  if (!arr.length) return null;
  
  const newArr = [...arr];
  let carry = 1;
  
  for (let i = newArr.length - 1; i > -1; --i) {
    if (newArr[i] < 0 || newArr[i] > 9) return null;
    
    
    if (newArr[i] + carry === 10) {
      newArr[i] = 0;
      continue;
    }
    if (carry === 1){
      newArr[i] += carry;
      carry--;
    }
  }
  
  if (carry) newArr.unshift(carry)
  return newArr;
  
}

It is happen stance that some parts look so similar