Javascript how to merge or combine array of days with same opening hours

Viewed 120

I have an array of days and the opening hours:

let arrData = [
{
  "dayOfTheWeek":"Monday",
  "isOpen":true,
  "hoursOfBusiness":[
     {
        "opensAt":"10:00:00",
        "closesAt":"15:00:00"
     }
  ]
},
{
  "dayOfTheWeek":"Tuesday",
  "isOpen":true,
  "hoursOfBusiness":[
     {
        "opensAt":"10:00:00",
        "closesAt":"15:00:00"
     }
  ]
},
{
  "dayOfTheWeek":"Wednesday",
  "isOpen":true,
  "hoursOfBusiness":[
     {
        "opensAt":"10:00:00",
        "closesAt":"15:00:00",
     }
  ]
},
{
  "dayOfTheWeek":"Thursday",
  "isOpen":true,
  "hoursOfBusiness":[
     {
        "opensAt":"09:00:00",
        "closesAt":"16:00:00",
     }
  ]
},
{
  "dayOfTheWeek":"Friday",
  "isOpen":true,
  "hoursOfBusiness":[
     {
        "opensAt":"10:00:00",
        "closesAt":"14:00:00",
     }
  ]
},
{
  "dayOfTheWeek":"Saturday",
  "isOpen":false,
  "hoursOfBusiness":[]
},
{
  "dayOfTheWeek":"Sunday",
  "isOpen":false,
  "hoursOfBusiness":[]
}
]

And I want to combine the days where the opening hours are the same. So, basically my goal is to display the opening hours like this:

Monday-Wednesday: 10-15
Thursday: 9-16
Friday: 10-14

So, I tried to to this:

function openingHours(data){
   let merged = [];
   let idx = -1;
   for (let i = 0; i < data.length; i++) {
     let day = data[i];
     if (
       idx == -1 ||
       merged[idx].hoursOfBusiness.opensAt != day.hoursOfBusiness.opensAt
     ) {
      merged.push({
        days: day.dayOfTheWeek,
        opensAt: day.hoursOfBusiness.opensAt,
        closesAt: day.hoursOfBusiness.closesAt,
      });
     idx++;
    } else {
      merged[idx].days.push(day.dayOfTheWeek);
    }
  }
 return merged;
}

but this doesn't really work. I have created a JSFIDDLE so please check it out.

Can someone help me out?

UPDATE

All answers were pretty useful but actually I need a different output after all, since the opening/closing hours can vary, I need something like:

data: [
   {
     days: "Monday-Wednesday",
     opensAt: "10:00",
     closesAt: "15:00
   },
   {
     days: "Thursday",
     opensAt: "09:00",
     closesAt: "16:00
   },
   {
     days: "Friday",
     opensAt: "10:00",
     closesAt: "14:00
   },
]

I hope someone could help me out?

3 Answers

You could first group days by open-close time inside one object and then modify that object to get the desired structure of days: time. This assumes that the input data is sorted by the correct days order otherwise you would have to add that sorting step.

let data = [{"dayOfTheWeek":"Monday","isOpen":true,"hoursOfBusiness":[{"opensAt":"10:00:00","closesAt":"15:00:00"}]},{"dayOfTheWeek":"Tuesday","isOpen":true,"hoursOfBusiness":[{"opensAt":"10:00:00","closesAt":"15:00:00"}]},{"dayOfTheWeek":"Wednesday","isOpen":true,"hoursOfBusiness":[{"opensAt":"10:00:00","closesAt":"15:00:00"}]},{"dayOfTheWeek":"Thursday","isOpen":true,"hoursOfBusiness":[{"opensAt":"09:00:00","closesAt":"16:00:00"}]},{"dayOfTheWeek":"Friday","isOpen":true,"hoursOfBusiness":[{"opensAt":"10:00:00","closesAt":"14:00:00"}]},{"dayOfTheWeek":"Saturday","isOpen":false,"hoursOfBusiness":[]},{"dayOfTheWeek":"Sunday","isOpen":false,"hoursOfBusiness":[]}]

const parseTime = str => str.split(':').slice(0, 2).join(':')
const daysRange = arr => [arr[0], arr.length > 1 ? arr[arr.length - 1] : null].filter(Boolean)

const merged = data.reduce((r, {
  dayOfTheWeek,
  hoursOfBusiness: hours,
  isOpen
}) => {
  if (isOpen) {
    hours.forEach(({ opensAt, closesAt }) => {
      const time = `${opensAt}-${closesAt}`;
      if (!r[time]) r[time] = {
        days: [],
        opensAt: parseTime(opensAt),
        closesAt: parseTime(closesAt)
      }
      r[time].days.push(dayOfTheWeek)
    })
  }

  return r;
}, {})

const result = Object.entries(merged)
  .map(([_, value]) => ({ ...value,
    days: daysRange(value.days).join('-')
  }))


console.log(result)

There are some issues with the data/code

  1. the hoursOfBusiness in the data is an array
  2. the hoursOfBusiness in merged item is not available - u have directly added the opensAt property to it
  3. Not all the items have data into it - hoursOfBusiness for saturday and sunday is empty array

Below is the updated code considering the data as you have provided, and also considering that in your merged object you don't need array

function openingHours(data){
  let merged = [];
  let idx = -1;
  for (let i = 0; i < data.length; i++) {
    let day = data[i];
    if(day.hoursOfBusiness.length == 0){
    continue
    }
    if (
      idx == -1 ||
      merged[idx].opensAt != day.hoursOfBusiness[0].opensAt
    ) {
      merged.push({
        days: [day.dayOfTheWeek],
        opensAt: day.hoursOfBusiness[0].opensAt,
        closesAt: day.hoursOfBusiness[0].closesAt,
      });
      idx++;
    } else {
      merged[idx].days.push(day.dayOfTheWeek);
    }
  }
  return merged;
}

console.log(openingHours(arrData))

You have some error in your code: First days must be array not string and then in else if part you may need to check day.isOpen.

And finally use map to show array with your format

Here is working snippet:

let arrData = [
    {
        "dayOfTheWeek": "Monday",
        "isOpen": true,
        "hoursOfBusiness": [
            {
                "opensAt": "10:00:00",
                "closesAt": "15:00:00"
            }
        ]
    },
    {
        "dayOfTheWeek": "Tuesday",
        "isOpen": true,
        "hoursOfBusiness": [
            {
                "opensAt": "10:00:00",
                "closesAt": "15:00:00"
            }
        ]
    },
    {
        "dayOfTheWeek": "Wednesday",
        "isOpen": true,
        "hoursOfBusiness": [
            {
                "opensAt": "10:00:00",
                "closesAt": "15:00:00",
            }
        ]
    },
    {
        "dayOfTheWeek": "Thursday",
        "isOpen": true,
        "hoursOfBusiness": [
            {
                "opensAt": "09:00:00",
                "closesAt": "16:00:00",
            }
        ]
    },
    {
        "dayOfTheWeek": "Friday",
        "isOpen": true,
        "hoursOfBusiness": [
            {
                "opensAt": "10:00:00",
                "closesAt": "14:00:00",
            }
        ]
    },
    {
        "dayOfTheWeek": "Saturday",
        "isOpen": false,
        "hoursOfBusiness": []
    },
    {
        "dayOfTheWeek": "Sunday",
        "isOpen": false,
        "hoursOfBusiness": []
    }
]


function openingHours(data) {
    let merged = [];
    let idx = -1;
    for (let i = 0; i < data.length; i++) {
        let day = data[i];
        if (
            idx == -1 || (day.hoursOfBusiness.length > 0 && merged[idx].opensAt != day.hoursOfBusiness[0].opensAt)
        ) {
            merged.push({
                days: [day.dayOfTheWeek],
                opensAt: day.hoursOfBusiness[0].opensAt,
                closesAt: day.hoursOfBusiness[0].closesAt,
            });
            idx++;
        } else if (day.isOpen) {
            merged[idx].days.push(day.dayOfTheWeek);
        }
    }
    return merged;
}
let result = openingHours(arrData);

let normalizeResult = result.map(t =>
({
    days: t.days.length == 1 ? t.days[0] : (t.days[0] + "-" + t.days[t.days.length - 1]),
    opensAt: t.opensAt.split(':').slice(0, 2).join(':'),
    closesAt: t.closesAt.split(':').slice(0, 2).join(':')
})
); 

console.log(normalizeResult);

Related