0

Trying to find the best way to create an array of objects where I get only the Maximum and Minimum values for twice a day (before 12:00pm noon and after 12:00pm noon). The result should still return in order of time.

The best way I know to do it is:

  1. First filter the data based on the day (I'm using moment.js)
  2. Then filter the day into 2 different arrays: beforeNoon[], afterNoon[]
  3. Then get the Maximum value of the arrays using reduce
  4. Then get the Minimum value of the arrays using reduce
  5. Combine the results into 1 array, and sort by time ascending

I was hoping there would be a much easier way to do this... let me know if you have any suggestions. Thanks in advance for any suggestions!

const data = [
  {"t":"2020-05-27 03:42","v":"2.151"},
  {"t":"2020-05-27 08:48","v":"3.125"},
  {"t":"2020-05-27 11:54","v":"4.106"},
  {"t":"2020-05-27 18:00","v":"5.094"},
  {"t":"2020-05-27 21:06","v":"4.088"},
  {"t":"2020-05-27 23:12","v":"3.090"},
  {"t":"2020-05-28 00:18","v":"2.098"},
  {"t":"2020-05-28 03:24","v":"1.114"},
  {"t":"2020-05-28 08:30","v":"2.136"},
  {"t":"2020-05-28 12:36","v":"3.166"},
  {"t":"2020-05-28 17:42","v":"4.202"},
  {"t":"2020-05-28 22:48","v":"5.245"}, 
];

// STEP 1:
// Filter data to only return specific date "2020-05-27"
const today = data.filter( d => moment(d.t).format('YYYY-MM-DD') === '2020-05-27');
console.log('TODAY= ' + JSON.stringify(today));

// STEP 2:
// Divide today's data into half
const todayBeforeNoon = today.filter(d => moment(d.t).format('HH') <= '11');
const todayAfterNoon  = today.filter(d => moment(d.t).format('HH')  > '11');
console.log('TODAY BEFORE NOON= ' + JSON.stringify(todayBeforeNoon));
console.log('TODAY AFTER NOON= ' + JSON.stringify(todayAfterNoon));

// STEP 3:
// Get the max value per half day
const beforeNoonMax = todayBeforeNoon.reduce( (a, b) => { return (a.v > b.v) ? a : b}, 0);
const afterNoonMax  = todayAfterNoon.reduce(  (a, b) => { return (a.v > b.v) ? a : b}, 0);
console.log('MAX BEFORE NOON= ' + JSON.stringify(beforeNoonMax));
console.log('MAX AFTER NOON= ' + JSON.stringify(afterNoonMax));

// STEP 4:
// Get the min value per half day
const beforeNoonMin = todayBeforeNoon.reduce( (a, b) => { return (a.v < b.v) ? a : b}, 0);
const afterNoonMin  = todayAfterNoon.reduce(  (a, b) => { return (a.v < b.v) ? a : b}, 0);
console.log('MIN BEFORE NOON= ' + JSON.stringify(beforeNoonMin));
console.log('MIN AFTER NOON= ' + JSON.stringify(afterNoonMin));

// STEP 5:
// Combine the results into 1 array, and sort by time?
// HELP? SUGGESTIONS?
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.26.0/moment.min.js" integrity="sha256-5oApc/wMda1ntIEK4qoWJ4YItnV4fBHMwywunj8gPqc=" crossorigin="anonymous"></script>

2 Answers 2

1

Here's an approach that uses a hash to organize the data. The hashing key is:

"yyyy-mm-dd-AM" or "yyyy-mm-dd-PM"

which places the data objects into sub-arrays that are easy to process.

const data = [
  { t: "2020-05-27 03:42", v: "2.151" },
  { t: "2020-05-27 08:48", v: "3.125" },
  { t: "2020-05-27 11:54", v: "4.106" },
  { t: "2020-05-27 18:00", v: "5.094" },
  { t: "2020-05-27 21:06", v: "4.088" },
  { t: "2020-05-27 23:12", v: "3.090" },
  { t: "2020-05-28 00:18", v: "2.098" },
  { t: "2020-05-28 03:24", v: "1.114" },
  { t: "2020-05-28 08:30", v: "2.136" },
  { t: "2020-05-28 12:36", v: "3.166" },
  { t: "2020-05-28 17:42", v: "4.202" },
  { t: "2020-05-28 22:48", v: "5.245" },
];

// create a hash object with properties
//    "yyyy-mm-dd-AM": [
//      {"t": "yyyy-mm-dd-AM hh:mm", "v": "1.234"},
//      ...
//    ],
//    "yyyy-mm-dd-PM": [],
//    ...
let hash = {};
data.forEach((tv) => {
  let [date, time] = tv.t.split(" ");
  let [hour, minute] = time.split(":");
  let meridian = hour >= 12 ? "-PM" : "-AM";
  let propName = date + meridian;
  hash[propName] = hash[propName] || [];
  hash[propName].push(tv);
});

// console.log("hash:", JSON.stringify(hash, null, 2));

let minmaxArray = [];

// iterate over each hash property
Object.values(hash).forEach((hashProp) => {

  // determine the min and max values in hashProp's array
  let vals = hashProp.map((tv) => tv.v);
  let vMin = Math.min(...vals);
  let vMax = Math.max(...vals);
  
  // push the min and max data elements onto array
  let tvMin = hashProp.filter((tv) => tv.v == vMin)[0];
  let tvMax = hashProp.filter((tv) => tv.v == vMax)[0];
  minmaxArray.push(tvMin);
  minmaxArray.push(tvMax);
});

// sort by date and time
minmaxArray.sort((a, b) => a.t.localeCompare(b.t));

const results = document.getElementById('results');
results.innerHTML = "minmaxArray: " +
  JSON.stringify(minmaxArray, null, 2);
Results:<br/>
<pre id="results"></pre>

Sign up to request clarification or add additional context in comments.

Comments

0

One of the advantages of the ISO 8601–like formatting is that string and number methods can be applied directly for sorting, grouping and comparisons, e.g.

let data = [
  {"t":"2020-05-27 03:42","v":"2.151"},
  {"t":"2020-05-27 08:48","v":"3.125"},
  {"t":"2020-05-27 11:54","v":"4.106"},
  {"t":"2020-05-27 18:00","v":"5.094"},
  {"t":"2020-05-27 21:06","v":"4.088"},
  {"t":"2020-05-27 23:12","v":"3.090"},
  {"t":"2020-05-28 00:18","v":"2.098"},
  {"t":"2020-05-28 03:24","v":"1.114"},
  {"t":"2020-05-28 08:30","v":"2.136"},
  {"t":"2020-05-28 12:36","v":"3.166"},
  {"t":"2020-05-28 17:42","v":"4.202"},
  {"t":"2020-05-28 22:48","v":"5.245"}, 
];

             
let result = Object.values(
  // Sort, not really necessary but just to make sure
  data.sort((a, b) => a.t.localeCompare(b.t))
  // Group by date and am/pm
  .reduce((acc, el) => {
      let [d, t] = el.t.split(' ');
      let dayPeriod = parseInt(t) < 12? 'am' : 'pm';
      let key = d + '_' + dayPeriod;
      acc[key]? acc[key].push(el) : acc[key] = [el];
      return acc;
    }, {})
  )
  // Get just max and min for am and pm
  .reduce((acc, el) => {
      // Sort dayPeriod group on temperature
      el.sort((a, b) => a.v - b.v);
      // Min will be first, max will be last
      acc.push(el[0], el[el.length - 1]);
    return acc;
  }, []);
             
console.log(result)

I'm not a fan though of chaining lots of function calls. I think it's better to split it into discrete operations so that it's easier to maintain and modify later. It's also easier to implement error checking for missing or out of bounds values.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.