DEV Community

Cover image for Codewars - Pick peaks
Nicolás Bost
Nicolás Bost

Posted on

Codewars - Pick peaks

Salutations.

Bill says hi

I'm posting Codewars challenges and my thought process in this series. I'm using JS and Node 18 whenever possible. Just for the sake of clarity, I'm making fair use of them.

I took a break, and now I'm back. Did some challenges without posting the solution here though. Let's approach an easy challenge.

Pick peaks is a fun one. You need to find local maxima according to its mathematical definition. From GFG:

Mathematically, f (a) ≥ f (a -h) and f (a) ≥ f (a + h) where h > 0, then a is called the Local maximum point.

In essence, we need to see which values are bigger than its closest neighbors. If a neighbor is missing we can't verify if it's a local maximum or not. So we won't check the borders of the array.

The following solution is not optimized. It should be one pass. Besides, I was taught to avoid using break and continue. But it does the job.

First we set the rules:

  • If the array is empty, return empty arrays. [] => {pos:[], peaks:[]}
  • If a value is less than or equal to the previous one, it's automatically discarded (plateaux will be treated in another rule). (array[i] <= array[i-1]) ? discard
  • If a value is NOT discarded by the previous rule, AND it's bigger than the next value, it's a maximum. (array[i] > array[i+1]) ? maximum
  • If a value is NOT discarded by the aforementioned rule, AND it's EQUAL TO the next value, it requires special treatment. We'll solve this later.

Second, it needs an specific return value: {pos:[], peaks:[]}
This challenge asks for position and value of the maxima.

Third, we need to set a loop for the array:
for (let i = 1 ; i < arr.length -1 ; i++)
We skip the first and last value because they'll never be maxima according to the definition.

Fourth, we implement the rules:

  for (let i = 1 ; i < arr.length -1 ; i++){
    if (arr[i] <= arr[i-1]){
      continue;
    }
    if (arr[i] > arr[i+1]){
      cache.pos.push(i);
      cache.peaks.push(arr[i]);
    }
    if (arr[i] == arr[i+1]){
      // TO DO
    }
  }
Enter fullscreen mode Exit fullscreen mode

We'll need to refine that last part. That's the special treatment mentioned above while setting the rules. It is merely another loop that acts as a sub process:

    if (arr[i] == arr[i+1]){
      for (let j=i +1 ; j< arr.length - 1; j++){
        if (arr[j] == arr[j+1]){
          continue;
        }
        if (arr[j] < arr[j+1]){
          break;
        }
        if (arr[j] > arr[j+1]){
          cache.pos.push(i);
          cache.peaks.push(arr[i]);
        }
      }
    }
Enter fullscreen mode Exit fullscreen mode

The sum of it all is this:

function pickPeaks(arr){
  let cache = {pos:[], peaks:[]};
  if (arr == false) {
    return cache;
  }

  for (let i = 1 ; i < arr.length -1 ; i++){
    if (arr[i] <= arr[i-1]){
      continue;
    }
    if (arr[i] > arr[i+1]){
      cache.pos.push(i);
      cache.peaks.push(arr[i]);
    }
    if (arr[i] == arr[i+1]){
      for (let j=i +1 ; j< arr.length - 1; j++){
        if (arr[j] == arr[j+1]){
          continue;
        }
        if (arr[j] < arr[j+1]){
          break;
        }
        if (arr[j] > arr[j+1]){
          cache.pos.push(i);
          cache.peaks.push(arr[i]);
        }
      }
    }
  }

  return cache;
}
Enter fullscreen mode Exit fullscreen mode

And now let's test it... Yay! It passed! Let's submit and...

Oh no. What???

Bill says hi

This particular test: pickPeaks([1,2,5,4,3,2,3,6,4,1,2,3,3,4,5,3,2,1,2,3,5,5,4,3])
This should return: {pos:[2,7,14,20], peaks:[5,6,5,5]}
It returns: {pos:[2,7,14,20,20], peaks:[5,6,5,5,5]}

But why? The logic is sound. And every loop is correctly... Uhmmm... Wait... It gets duplicated. Position 20, value 5. It's there twice. There's got be something wrong here:

    if (arr[i] == arr[i+1]){
      for (let j=i +1 ; j< arr.length - 1; j++){
        if (arr[j] == arr[j+1]){
          continue;
        }
        if (arr[j] < arr[j+1]){
          break;
        }
        if (arr[j] > arr[j+1]){
          cache.pos.push(i);
          cache.peaks.push(arr[i]);
        }
      }
    }
Enter fullscreen mode Exit fullscreen mode

After some debugging with Dev Tools, I found it. Here's the problem:

        if (arr[j] > arr[j+1]){
          cache.pos.push(i);
          cache.peaks.push(arr[i]);
        }
Enter fullscreen mode Exit fullscreen mode

It's missing a break statement. [...3,5,5,4,3] duplicates the second value because it only breaks out of the inner loop when it finds a sequence where this exit condition occurs:

        if (arr[j] < arr[j+1]){
          break;
        }
Enter fullscreen mode Exit fullscreen mode

Otherwise it keeps going. Turns out it should exit when it finds a maximum too:

        if (arr[j] > arr[j+1]){
          cache.pos.push(i);
          cache.peaks.push(arr[i]);
          break; // <------------ EXTREMELY IMPORTANT
        }
Enter fullscreen mode Exit fullscreen mode

FIXED:

function pickPeaks(arr){
  let cache = {pos:[], peaks:[]};
  if (arr == false) {
    return cache;
  }

  for (let i = 1 ; i < arr.length -1 ; i++){
    if (arr[i] <= arr[i-1]){
      continue;
    }
    if (arr[i] > arr[i+1]){
      cache.pos.push(i);
      cache.peaks.push(arr[i]);
    }
    if (arr[i] == arr[i+1]){
      for (let j=i +1 ; j< arr.length - 1; j++){
        if (arr[j] == arr[j+1]){
          continue;
        }
        if (arr[j] < arr[j+1]){
          break;
        }
        if (arr[j] > arr[j+1]){
          cache.pos.push(i);
          cache.peaks.push(arr[i]);
          break;  // if you don't break, situations like this: 3,5,5,4,3
                  // can duplicate a value..
        }
      }
    }
  }

  return cache;
}
Enter fullscreen mode Exit fullscreen mode

Inefficient, but effective.

Take care. Drink water 💧💧💧.

Previous

Hostinger image

Get n8n VPS hosting 3x cheaper than a cloud solution

Get fast, easy, secure n8n VPS hosting from $4.99/mo at Hostinger. Automate any workflow using a pre-installed n8n application and no-code customization.

Start now

Top comments (0)

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay