Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get the biggest chronological drop, min and max from an array with O(n)

I have written a javascript function for analyzing the biggest drop in an array. But one little issue is still there. As the max value, I always get max value from my hole array and not from my drop.

Example: Array: [100,90,80,120]

The biggest drop would be between 100 and 80. So max must be 100, and min 80. My function always returns the highest value from the whole array. in my case 120

function checkData(data) {
  let max = 0
  let min = 0
  let drop = 0

  for (let i = 0; i < data.length; i++) {
      if (max < data[i]) {
          max = data[i] //?
      } else {
          let tempDrop = max - data[i]
          drop = Math.max(tempDrop, drop)
          min = max - drop
      }
  }
  return [max, min, drop]
}

I want to get the chronological correct biggest delta from left to right Demo Image

like image 864
Pommesloch Avatar asked Nov 26 '18 12:11

Pommesloch


2 Answers

Your loop should keep track of the current drop and compare it to the previous largest drop. You can do this by tracking indexes:

function checkData(data) {
  let bestDropStart = 0
  let bestDropEnd = 0
  let bestDrop = 0
  let currentDropStart = 0
  let currentDropEnd = 0
  let currentDrop = 0
  for (let i = 1; i < data.length; i++) {
    if (data[i] < data[i - 1]) {
      // we are dropping
      currentDropEnd = i
      currentDrop = data[currentDropStart] - data[i]
    } else {
      // the current drop ended; check if it's better
      if (currentDrop > bestDrop) {
        bestDrop = currentDrop
        bestDropStart = currentDropStart
        bestDropEnd = currentDropEnd
      }
      // start a new drop
      currentDropStart = currentDropEnd = i
      currentDrop = 0
    }
  }
  // check for a best drop at end of data
  if (currentDrop > bestDrop) {
    bestDrop = currentDrop
    bestDropStart = currentDropStart
    bestDropEnd = currentDropEnd
  }
  // return the best drop data
  return [data[bestDropStart], data[bestDropEnd], bestDrop]
}

console.log(checkData([100, 90, 80, 120]))
console.log(checkData([100, 90, 80, 120, 30]))
console.log(checkData([70, 100, 90, 80]))
console.log(checkData([100, 90, 80, 120, 30, 50]))

You can also do it by just keeping the start and end values for the current and best drops, but my preference would be to explicitly track the indexes. It just seems clearer (easier to debug and maintain) to me that way.

like image 61
Ted Hopp Avatar answered Sep 19 '22 08:09

Ted Hopp


It sounds like you'd like the returned max and min to be the same ones used in calculating the largest drop rather than the overall max and min. In that case just add an additional variable to store those when drop is updated. Replace

drop = Math.max(tempDrop, drop)

with an if statement (pseudocode):

if current_drop is greater than stored_drop:
  stored_drop = current_drop
  stored_max_min = current max and min

then return the additional stored max and min.

like image 38
גלעד ברקן Avatar answered Sep 22 '22 08:09

גלעד ברקן