Codewars: Code warrior

Find the Next Perfect Square

by Sep 9, 2018Code Warrior

Graphic: Code warrior banner - Amy Coleman

This post is the first in a series of Codewars challenges, or kata, that I’ll be sharing. The goal with these shared kata is to demonstrate how to solve and refactor a unique programming problem for readability and efficiency.

Codewars: About this kata

The foundation of programming in any language is learning how to iterate over arrays of data by incrementing numbers and how to find results in those arrays by matching a condition. Codewars provides this JavaScript-based kata to train budding developers to use algebra to find a result.

What this Codewars challenge specifically does is prepare a developer for cases where there is no array to iterate over – and possibly no state to match if you’re a Reactive dev – to find the next result. For this particular problem, basic algebra is required to find a solution.

GIF: Adventure TimeGiphy

Codewars Kata Instructions

You might know some pretty large perfect squares. But what about the NEXT one?

Complete the findNextSquare method that finds the next integral perfect square after the one passed as a parameter. Recall that an integral perfect square is an integer n such that sqrt(n) is also an integer.

If the parameter is itself not a perfect square, than -1 should be returned. You may assume the parameter is positive.

Examples:

findNextSquare(121) –> returns 144
findNextSquare(625) –> returns 676
findNextSquare(114) –> returns -1 since 114 is not a perfect

Solving this Codewars challenge

Before you can solve any problem you need to understand what a solution needs to accomplish in order to be acceptable. Once you understand what you need to do you can map out your solution and figure out which tools will work for the job. After you implement those tools in a first pass, you’ll think about whether there’s a simpler or more efficient solution.

Writing Acceptance criteria

It may feel tedious to do this for a little Codewars challenge part but re-writing a problem as acceptance criteria is good practice for translating spec. Eventually as a developer you end up writing tests for pieces of code, so writing acceptance criteria is also a great way to write out the checks that a test needs to perform.

For this problem, our acceptance criteria looks like this:

  • Accepts an integer sq
  • Returns the next perfect square integer if sq is a perfect square
  • Returns a result of -1 if sq is not a perfect square

Using acceptance criteria to select tools

Writing acceptance criteria also makes it easier to figure out which JavaScript tools will be necessary to solve the problem. A few terms stick out in our acceptance criteria that tell us how this problem will need to be solved:

  • if – tells us that an if...else conditional statement will be used
  • whole integer – tells us that we’ll need to use % (which is a modulo operator, not a percent) to ensure that a number is a whole number and not a decimal
  • square – tells us that we’ll need to use the Math object with the sqrt function
  • next – tells us that we’ll need to increment some number with ++ or +1 to get a result

Writing a code skeleton with acceptance criteria

Now that we have acceptance criteria written and a list of tools we can use to meet that criteria, we can write out a code skeleton to help organize our process.

This step does even more than keep you organized. Commented code skeletons also get you in the habit of documenting your code so that other developers can easily read it and understand what you were trying to accomplish in case something needs to be debugged or refactored later on.

Here’s what our code skeleton looks like:

function findNextSquare(sq) {

// use a conditional statement to see if sq is a perfect square
if(){
// if it is, increment sq until the next the perfect square is found
}else{
// if it isn’t, return -1
return -1
}

}

Is it a perfect square?

When you multiply a whole integer by itself, you get a perfect square. Testing a number to see if it’s a perfect square is easy. If the square root of an integer is a decimal, then it’s not a perfect square. So in our code we need to find the square root of sq and then check and see if it’s a decimal number or not.

The Math.sqrt() function gives us the square root of sq. All we need to do to evaluate the square root of sq is stick it in the function: Math.sqrt(sq). But how do we figure out if that number is a whole number and not a decimal?

The modulo operator, or %, takes a number and divides it by a multiple and returns the remainder if there is one. a % b === c would evaluate number a divided by multiple b and return the remainder c. When a modulo operation returns a remainder greater than 0, it means that dividing the number by the multiple results in a decimal.

Whole numbers are always a multiple of 1, so a modulo operation should return a 0 if the number is a whole number. Our conditional statement, the if of the if..else, is therefore Math.sqrt(sq) % 1 === 0. We can pop that in now.

function findNextSquare(sq) {

// use a conditional statement to see if sq is a perfect square
if(Math.sqrt(sq) % 1 === 0){
// if it is, increment sq until the next the perfect square is found
}else{
// if it isn’t, return -1
return -1
}

}

Incrementing to the next perfect square

The easy work is done. Now we have to figure out what the next perfect square is. The most obvious approach is to reuse code we’ve already written with some small modifications.

We know that we want to increment sq using sq++ until the next perfect square is found. A do...while loop would serve us well if this was the best approach. So we’ll say that sq should be incremented as long as our original conditional isn’t true.

It’ll look like this:

function findNextSquare(sq) {

// use a conditional statement to see if sq is a perfect square
if(Math.sqrt(sq) % 1 === 0){
// if it is, increment sq until the next the perfect square is found
do {
sq++
}while (Math.sqrt(sq) % 1 !== 0)
return sq
}else{
// if it isn’t, return -1
return -1
}

}

Incrementing the square instead

The obvious approach has yielded a few lines of code. And here’s where we ask ourselves – can we reduce the amount of code being written? If so, how?

Maybe you’ve already figured out an easier way to approach this problem. But for the rest of you, I want to show you how expanding your toolbox with better math skills and logic can yield more efficient results.

We just have to think about what we know about perfect squares. If the square root of sq is a whole number, then the easiest way to find the next perfect square is to just add 1 to the square root of sq and then find the square of that number.

To do this we’ll use Math.pow() to square sq + 1:

function findNextSquare(sq) {

// use a conditional statement to see if sq is a perfect square
if(Math.sqrt(sq) % 1 === 0){
// if it is, increment the square root of sq by 1 and return the square
return Math.pow(Math.sqrt(sq)+1,2)
}else{
// if it isn’t, return -1
return -1
}

}

Single line ternary operator

This code is great for readability and documentation. But it’s also a lot of lines for a really simple operation. Codewars has different levels of best practices that you can challenge yourself to meet for any given problem, and one of those is the single-line implementation.

There is actually a way to get this down to a single line of code by using a ternary operator to write the conditional statement. I love ternaries. And the more you write them the better you get at reading them – making JavaScript written by others more accessible to you.

A conditional statement written with a ternary operator starts with a true / false statement followed by instructions for what to do when the statement is true or false.

Think about it like taking a statement like this:
if this is soda I will drink it or else I will not drink it

and shortening it to this:
Soda? Drink : don't drink.

If we refactor our code with a ternary, it will look like this:

function findNextSquare(sq) {

// use a ternary to see if sq is a perfect square, return next perfect square if true and -1 if false
return Math.sqrt(sq) % 1 === 0 ? Math.pow(Math.sqrt(sq)+1,2) : -1;

}

Wrapping up

There are many ways to approach this Codewars problem and I’ve only shown a couple. Some people store variables and create multiple functions, and that may be necessary for the project you’re working on. For me, I think going for readability, clean scope, and efficiency is the best. But different teams will have different best practices.

No matter what level of developer you are you should work on good habits whenever you can. Get practice translating spec into acceptance criteria, documenting your process, and refactoring for readability and efficiency as early as possible.

And if you struggle with math – don’t despair. I went through high school thinking I was terrible at math. But I later discovered that I wasn’t bad at math when I took a really great math course – basically a remedial math course that got you from pre algebra through basic statistics – and discovered that I just learned math differently from the way it was taught in my high school. I ended up minoring in mathematics and getting into dynamic systems theory.

We’re all interest-driven learners. When a solution matters to us, we will find out how to get to it. So if you’re the type of person who thinks you’re bad at math, try a new approach to learning it. You’ll get there, I promise.

Oh, and keep practicing in Codewars! It’ll make you a better dev in the long run!

GIF: Adventure TimeGiphy