madhubala
madhubala

Reputation: 45

How to split array of object time series to multiple time series based on Time slots?

I want to split my Start and End Time data on the basis of hourly time slots.

input = [
  //Type A, it lies between 7 to 8 time slot
  { "start_time": "2021-08-20T07:00:00.000", "end_time": "2021-08-20T07:25:00.000", "Type": "A" },
  //Type B , Time lies between 8, 9 time slot, so need to split into two new objects
  { "start_time": "2021-08-20T07:25:00.000", "end_time": "2021-08-20T08:39:49.000", "Type": "B" },
  //Type C, Time lies between three slots 8, 9, 10, and 11. SO need to split in 4 objects.
  { "start_time": "2021-08-20T08:39:50.000", "end_time": "2021-08-20T11:02:34.000", "Type": "C" }
]

Expected output:

var output = [
  { "start_time": "2021-08-20T07:00:00.000", "end_time": "2021-08-20T07:25:00.000", "Type": "A" },
  { "start_time": "2021-08-20T07:25:00.000", "end_time": "2021-08-20T08:00:00.000", "Type": "B" },
  { "start_time": "2021-08-20T08:00:00.000", "end_time": "2021-08-20T08:39:49.000", "Type": "B" },
  { "start_time": "2021-08-20T08:39:49.000", "end_time": "2021-08-20T09:00:00.000", "Type": "C" },
  { "start_time": "2021-08-20T09:00:00.000", "end_time": "2021-08-20T10:00:00.000", "Type": "C" },
  { "start_time": "2021-08-20T10:00:00.000", "end_time": "2021-08-20T11:00:00.000", "Type": "C" },
  { "start_time": "2021-08-20T11:00:00.000", "end_time": "2021-08-20T11:02:34.000", "Type": "C" }
] 

I used moment-range to create time Slots.

const day_start=moment().startOf('day').hours(7);
const day_end=moment().startOf('day').hours(15)
const day=moment.range(day_start,day_end)
const time_slots = Array.from(day.by('minutes',{step:60}))

Output I get here :

    const hourly_data = [moment("2021-08-20T07:00:00.000"),
moment("2021-08-20T08:00:00.000"),
moment("2021-08-20T09:00:00.000"),

...
moment("2021-08-20T13:00:00.000"),
moment("2021-08-20T14:00:00.000"),
moment("2021-08-20T1500:00.000")]

I got hourly range as required, but unable to split.

Any help to get above output?

Upvotes: 2

Views: 832

Answers (4)

Rahul Kumar
Rahul Kumar

Reputation: 3157

Use Array.reduce() to iterate through each object and calculate time difference b/w start_time and end_time in minutes using moment, and then create time slots by calculating remaining minutes inside while loop.

input = [
  { "start_time": "2021-08-20T07:00:00.000", "end_time": "2021-08-20T07:25:00.000", "Type": "A" },
  { "start_time": "2021-08-20T07:25:00.000", "end_time": "2021-08-20T08:39:49.000", "Type": "B" },
  { "start_time": "2021-08-20T08:39:50.000", "end_time": "2021-08-20T11:02:34.000", "Type": "C" }
];

const result = input.reduce((arr, {start_time, end_time, Type}) => {
  const dayStart = moment.utc(start_time);
  const dayEnd = moment.utc(end_time);
  let timeDiff = moment.duration(dayEnd.diff(dayStart)).asMinutes();
  if (timeDiff > 60) {
    while(dayStart < dayEnd) {
      const diff = moment.duration(dayEnd.diff(dayStart)).asMinutes();
      const minutes = diff > 60 ? 60 : diff;
      const remainder = minutes - dayStart.minute();
      const newObj = {
        start_time: dayStart.clone(),
        Type
      };
      dayStart.add(remainder, 'minutes');
      newObj.end_time = dayStart.clone();
      arr.push(newObj);
    }
  } else {
     arr.push({
      start_time: dayStart,
      end_time: dayEnd,
      Type
     });
  }
  return arr;
}, []);

console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.20.1/moment.min.js"></script>

Upvotes: 2

AlTheLazyMonkey
AlTheLazyMonkey

Reputation: 1242

If you want the output write in your question you can try with this. But you can do that whitout use range.

In every case you must check if the end time of the slot is before or after the limit of the range and use the lower

window['moment-range'].extendMoment(moment); // just declaration

const input = [
  //Type A, it lies between 7 to 8 time slot
  { "start_time": "2021-08-20T07:00:00.000", "end_time": "2021-08-20T07:25:00.000", "Type": "A" },
  //Type B , Time lies between 8, 9 time slot, so need to split into two new objects
  { "start_time": "2021-08-20T07:25:00.000", "end_time": "2021-08-20T08:39:49.000", "Type": "B" },
  //Type C, Time lies between three slots 8, 9, 10, and 11. SO need to split in 4 objects.
  { "start_time": "2021-08-20T08:39:50.000", "end_time": "2021-08-20T11:02:34.000", "Type": "C" }
]
const output = [];

for (const rangeDetails of input) {
  const { start_time, end_time, Type } = rangeDetails;
  const dateTimeStart = moment(start_time);
  const dateTimeEnd = moment(end_time);

  const range = moment.range(dateTimeStart, dateTimeEnd);

  for (const startDateSlot of range.by('minutes', { step: 60 })) {
    const help = { Type };
    help.start_time = startDateSlot.clone();
    help.end_time = moment(startDateSlot).add(60, 'minutes').isBefore(dateTimeEnd) ? moment(startDateSlot).add(60, 'minutes') : dateTimeEnd.clone();

    output.push(help);
  }
}
console.log(output);
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js"></script>
<script src="https://cdn.tutorialjinni.com/moment-range/4.0.2/moment-range.js"></script>

The alternative without range is there:

const input = [
  //Type A, it lies between 7 to 8 time slot
  { 'start_time': '2021-08-20T07:00:00.000', 'end_time': '2021-08-20T07:25:00.000', 'Type': 'A' },
  //Type B , Time lies between 8, 9 time slot, so need to split into two new objects
  { 'start_time': '2021-08-20T07:25:00.000', 'end_time': '2021-08-20T08:39:49.000', 'Type': 'B' },
  //Type C, Time lies between three slots 8, 9, 10, and 11. SO need to split in 4 objects.
  { 'start_time': '2021-08-20T08:39:50.000', 'end_time': '2021-08-20T11:02:34.000', 'Type': 'C' }
];

const output = [];

for (const rangeDetails of input) {
  const { start_time, end_time, Type } = rangeDetails;

  const dateTimeStart = moment(start_time);
  const dateTimeEnd = moment(end_time);

  while (dateTimeStart.isBefore(dateTimeEnd)) {
    const help = { Type, start_time: dateTimeStart.clone() }
    dateTimeStart.add(60, 'minutes'); // This edit dateTimeStart, adding 60 minutes

    if (dateTimeStart.isAfter(dateTimeEnd)) help.end_time = dateTimeEnd.clone();
    else help.end_time = dateTimeStart;

    output.push(help);
  }
}
console.log(output);
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js"></script>

Upvotes: 4

diealtebremse
diealtebremse

Reputation: 333

You could check how many hours each time span cuts by calculating its minutes divided by 60 (minutes). Now you know how often you have to split. For every time span you have to split there are three cases:

  1. First split: starts with start_time and ends with start_time plus one hour
  2. Other split: starts with last items end and ends with plus one hour
  3. Last split: starts with last items end and ends with end_time

Maybe there is a more efficient way, but the below code does work:

const input = [
    { "start_time": "2021-08-20T07:00:00.000", "end_time": "2021-08-20T07:25:00.000", "Type": "A" },
    { "start_time": "2021-08-20T07:25:00.000", "end_time": "2021-08-20T08:39:49.000", "Type": "B" },
    { "start_time": "2021-08-20T08:39:50.000", "end_time": "2021-08-20T11:02:34.000", "Type": "C" }
]

const output = []

const format = "YYYY-MM-DDTHH:mm:ss.SSS"

input.forEach(span => {
    const end = moment(span.end_time)
    const start0 = moment(span.start_time).minutes(0).seconds(0)
    const duration = moment.duration(end.diff(start0))
    const minutes = duration.asMinutes()
    const countSplits = Math.ceil(minutes / 60)

    if(countSplits > 1) {
        for (let index = 0; index < countSplits; index++) {
            if(index === 0) {
                output.push({
                    "start_time": span.start_time,
                    "end_time": moment(span.start_time).add(1, 'hour').minutes(0).seconds(0).format(format),
                    "Type": span.Type
                })
            }
            else if(index < countSplits - 1) {
                output.push({
                    "start_time": moment(span.start_time).add(index, 'hour').minutes(0).seconds(0).format(format),
                    "end_time": moment(span.start_time).add(index + 1, 'hour').minutes(0).seconds(0).format(format),
                    "Type": span.Type
                })
            }
            else {
                output.push({
                    "start_time": moment(span.start_time).add(index, 'hour').minutes(0).format(format),
                    "end_time": span.end_time,
                    "Type": span.Type
                })
            }
        }
    }
    else {
        output.push(span)
    }
})

console.log(output)
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js" integrity="sha512-qTXRIMyZIFb8iQcfjXWCO8+M5Tbc38Qi5WzdPOYZHIlZpzBHG3L3by84BBBOiRGiEb7KKtAOAs5qYdUiZiQNNQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>

Upvotes: 2

IVO GELOV
IVO GELOV

Reputation: 14259

You need to compare each input slot with each of your standard time slots and compute their overlapping - thus you will automatically split the bigger input slots.

var standardTimeSlots = [
  {start_time: "07:00:00", end_time: "08:00:00"},
  {start_time: "08:00:00", end_time: "09:00:00"},
  {start_time: "09:00:00", end_time: "10:00:00"},
  {start_time: "10:00:00", end_time: "11:00:00"},
  {start_time: "11:00:00", end_time: "12:00:00"},
];

var inputSlots = [
//Type A, it lies between 7 to 8 time slot
  { "start_time": "2021-08-20T07:00:00.000", "end_time": "2021-08-20T07:25:00.000", "Type": "A" },
  //Type B , Time lies between 8, 9 time slot, so need to split into two new objects
  { "start_time": "2021-08-20T07:25:00.000", "end_time": "2021-08-20T08:39:49.000", "Type": "B" },
  //Type C, Time lies between three slots 8, 9, 10, and 11. SO need to split in 4 objects.
  { "start_time": "2021-08-20T08:39:50.000", "end_time": "2021-08-20T11:02:34.000", "Type": "C" }
];

function computeOverlap(start_1, end_1, start_2, end_2)
{
  if (start_1 < end_2 && start_2 < end_1)
  {
    return [
      start_1 > start_2 ? start_1 : start_2,
      end_1 < end_2 ? end_1 : end_2
    ];
  }
  else
  {
    return false;
  }
}

var indexInput, indexStandard, tmpStart, tmpEnd, overlap;

for(indexInput = 0; indexInput < inputSlots.length; indexInput++)
{
  for(indexStandard = 0; indexStandard < standardTimeSlots.length; indexStandard++)
  {
    overlap = computeOverlap(
      inputSlots[indexInput].start_time.substr(11, 8), 
      inputSlots[indexInput].end_time.substr(11, 8),
      standardTimeSlots[indexStandard].start_time,
      standardTimeSlots[indexStandard].end_time,
    );
    if (overlap) console.log(
      inputSlots[indexInput].start_time.substr(0, 11) + overlap[0] + inputSlots[indexInput].start_time.substr(19), 
      inputSlots[indexInput].end_time.substr(0, 11) + overlap[1] + inputSlots[indexInput].end_time.substr(19))
  }
}

Upvotes: 1

Related Questions