Search code examples
javascriptarraystimeoverlap

How could I compute a list of "open hours" in javascript from a list of start and end times with overlaps?


I am trying to simplify a list of open times so that there are no overlaps/duplicate information showing (in javascript).

Here is an example of what I am trying to achieve:

The starting array would look something like this:

const mondayHours = [
  { start: "09:00", end: "14:00" },
  { start: "10:00", end: "15:00" },
  { start: "17:00", end: "23:00" },
];

And the data is currently displayed like: Open: 9am-2pm, 10am-3pm, 5pm-11pm

I want the result to return an array for the total open hours like so:

const computedMondayHours = [
  { start: "09:00", end: "15:00" },
  { start: "17:00", end: "23:00" },
];

And so that the data will be displayed like:

Open: 9am-3pm, 5pm-11pm

I have found a solution online that returns the latest open times with the earliest close times, thinking I could convert it for my uses, but that has not worked at all:

const hours = [{
    start: "09:00",
    end: "14:00"
  },
  {
    start: "10:00",
    end: "15:00"
  },
  {
    start: "17:00",
    end: "23:00"
  }
]

const isBetween = (value, start, end) => value > start && value < end
const computeOpenHours = (dayHours) => {
  const index = {}
  dayHours.forEach(({
    start: aStart,
    end: aEnd
  }) => {
    dayHours.forEach(({
      start: bStart,
      end: bEnd
    }) => {
      aStart = isBetween(bStart, aStart, aEnd) && bStart > aStart ? bStart : aStart
      aEnd = isBetween(bEnd, aStart, aEnd) && bEnd < aEnd ? bEnd : aEnd
    })
    const key = `${aStart}-${aEnd}`
    const value = {
      start: aStart,
      end: aEnd
    }
    index[key] = index[key] || value
  })
  return Object.keys(index).map(k => index[k])
}

console.log(computeOpenHours(hours))


Solution

  • const hours = [{"start":"09:00","end":"14:00"},
                   {"start":"10:00","end":"15:00"},
                   {"start":"17:00","end":"23:00"}]
    
    // returns -ve if a<b, 0 if a=b, +ve if a>b
    function cmp(a, b) {
      return a.replace(':','') - b.replace(':','')
    }
    function mergeIfOverlap({start:s1, end:e1}, {start:s2, end:e2}) {
      return cmp(s1,s2)<=0 && cmp(e1,s2)>=0
        && {start: s1, end: cmp(e1,e2)>=0 ? e1 : e2}
    }
    
    const result = [...hours].sort(({start:a},{start:b})=>cmp(a,b))
      .reduce((a,c,_,x,y)=>(
        x=a.findIndex(i => y=mergeIfOverlap(i,c)||mergeIfOverlap(c,i)),
        y && (a[x]=y) || a.push(c), a), [])
    
    console.log(result)