Javascript recursion and pattern matching with spread operator

Problem

Given an array of measurements ordered by time we have to implement a function that returns an array which will be filled with null measurements for every missing one.

In particular we want at least a measure for each hour after the time the first measure was taken.

This is necessary since we want to display this data in a chart which will try to connect this values with a line and we want to highlight missing measurements problems like in the following image.

Example

input:


[
 ["2017-05-01 : 09:30", 45.6 ],
 ["2017-05-01 : 10:50", 58.6 ],
 ["2017-05-01 : 12:30", 65.6 ],
 ["2017-05-01 : 15:30", 45.6 ]
]

output:


[
 ["2017-05-01 : 09:30", 45.6 ],
 ["2017-05-01 : 10:30", null ],
 ["2017-05-01 : 10:50", 58.6 ],
 ["2017-05-01 : 11:50", null ],
 ["2017-05-01 : 12:30", 45.6 ],
 ["2017-05-01 : 13:30", null ],
 ["2017-05-01 : 14:30", null ],
 ["2017-05-01 : 15:30", 45.6 ]
]

NB. time is expressed as string for clarity

Data structure

measurements       :: Array [ [timeInMilliseconds, value], ... ]
timeInMilliseconds :: Integer
value              :: Float

Code


function normalize(measurements) {
  const [first, ...rest] = measurements;
  return [first, ...addMeasureForEveryMissingHours(rest, moment(first[0]))]
}

function addMeasureForEveryMissingHours(measurements, start) {
  if(measurements.length === 0) return measurements;
  const [next, ...rest] = measurements;
  const nextTime        = moment(next[0]);
  const difference      = moment.duration(nextTime.diff(start)).hours() - 1;
  const missing         = _.times(
                            Math.max(0, difference), 
                            hour => [moment(start).add(hour + 1, "hours").valueOf(), null]
                          );
  return [...missing, next, ...addMeasureForEveryMissingHours(rest, nextTime)];
}

Solution

normalize performs pattern matching against the measurements getting the first measure and return an array containing first followed by the application of addMeasureForEveryMissingHours to the rest of the array passing the moment version of the first measure time.

The addMeasureForEveryMissingHours function is recursive so we split de definition in

  • Ground case:  return en empty array if there are no measurements taken
  • Recursive case:
  1. perform pattern match on the measurements using the spread operator get us the next measure and the rest measures
  2. calculates the difference in hours from the start and the next measure time
  3. creates an array of null measurements for every missing hour (using lodash times) between  start and next
  4. again using the spread operator return an array containing the missing measurements followed by next and the application of recursion on rest and nextTime as the new start

Observation

This is a very simple example showing how powerful Javascript has become with the spread operator allowing us to solve problems in an elegant way.

Leave a Reply

Please Login to comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.