Search code examples
javascriptarraysscheduledays

Convert arrays of active days to readable string


I need to convert an array of boolean values indicating whether the store is open on a given day.

For example:

Case 1:

Input data: [true, true, true, true, true, true, true]
Expected output: Every day

Case 2:

Input data: [true, true, true, true, true, false, false]
Expected output: Mon-Fri

Case 3:

Input data: [true, true, false, false, true, true, true]
Expected output: Mon-Tue, Fri-Sun

Case 4:

Input data: [true, false, false, true, false, false, true]
Expected output: Mon, Thu, Sun

Case 5:

Input data: [true, true, false, true, true, true, false]
Expected output: Mon-Tue, Thu-Sat

Case 6:

Input data: [true, false, false, false, false, false, false]
Expected output: Only Monday

I came up with this, but need help with cases 2-5

const daysLabels = [
  { label: "Monday", short: "Mon" },
  { label: "Tuesday", short: "Tue" },
  { label: "Wednesday", short: "Wed" },
  { label: "Thursday", short: "Thu" },
  { label: "Friday", short: "Fri" },
  { label: "Saturday", short: "Sat" },
  { label: "Sunday", short: "Sun" }
];

const getSchedule = ({ case: days }) => {
  let activeDays = [];
  for (let i = 0; i < [...days].length; i++) {
    const day = [...days][i];
    if (day) {
      activeDays.push({ ...daysLabels[i], value: day });
    }
  }

  if (activeDays.length === 7) {
    return "Every day";
  }

  if (activeDays.length === 1) {
    return `Only ${activeDays[0].label}`;
  }

  return "#TODO";
};

Sandbox - link


Solution

  • You can use Array.reduce() to create groups of day ranges, along with the correct labels.

    We then use a Array.map() call to return only the label for each range.

    I've added the 6 test cases mentioned, they should all pass.

    const daysLabels = [
      { label: "Monday", short: "Mon" },
      { label: "Tuesday", short: "Tue" },
      { label: "Wednesday", short: "Wed" },
      { label: "Thursday", short: "Thu" },
      { label: "Friday", short: "Fri" },
      { label: "Saturday", short: "Sat" },
      { label: "Sunday", short: "Sun" }
    ];
    
    function getDayRange(input) {
        // Deal with 7 days and 1 day only first...
        if (input.filter(active => active).length === 7) { 
            return 'Every day';
        } else if (input.filter(active => active).length === 1) { 
            return `Only ${daysLabels[input.findIndex(active => active)].label}`;
        }
        // 2 - 6 days active
        return input.reduce((acc, active, idx) => {
            if (active) {
                if (!acc.length || acc[acc.length - 1].end < (idx - 1) ) { 
                    acc.push({ start: idx, end: idx, label: daysLabels[idx].short, startLabel: daysLabels[idx].short });
                } else {
                    acc[acc.length - 1].end = idx;
                    acc[acc.length - 1].label = acc[acc.length - 1].startLabel + '-' + daysLabels[idx].short; 
                }
            }
            return acc;
        }, []).map(r => r.label).join(', ');
    }
    
    const cases = [
        { input: [true, true, true, true, true, true, true], expected: 'Every day' },
        { input: [true, true, true, true, true, false, false], expected: 'Mon-Fri' },
        { input: [true, true, false, false, true, true, true], expected: 'Mon-Tue, Fri-Sun' },
        { input: [true, false, false, true, false, false, true], expected: 'Mon, Thu, Sun' },
        { input: [true, true, false, true, true, true, false], expected: 'Mon-Tue, Thu-Sat' },
        { input: [true, false, false, false, false, false, false], expected: 'Only Monday' },
    ]
    console.log(`Case`, '\t', 'Pass', '\t', 'Output')
    cases.forEach(({ input, expected }, idx) => {
        let output = getDayRange(input);
        console.log(`${idx + 1}`, '\t', output === expected, '\t', output)
    })
    .as-console-wrapper { max-height: 100% !important; }