title: Generate deeply nested object based on an array of dates and the dynamic arguments passed to the function
body: <p>What I am trying to achieve is, based on a provided array of dates, and a number of dynamic arguments to the function (max 10), to group those days by year, quarter, month, day, time etc. based on the <code>format</code> function arguments provided by date-fns (<a href="https://date-fns.org/v2.27.0/docs/format" rel="nofollow noreferrer">https://date-fns.org/v2.27.0/docs/format</a>). The function <code>getFormattedDates</code> currently looks like this:</p>
  <pre><code>
  const groupObject = (
    array: Date[],
    path: string | undefined,
    key: string,
    ...rest: string[]
  ) =&gt;
    array.reduce((acc, item: Date) =&gt; {
      const firstPath = path?.split('.')[0];
      return {
        ...acc,
        [format(item, firstPath || key)]: rest.length
          ? {
              ...acc[format(item, firstPath || key)],
              ...groupObject([item], rest.join('.'), ...rest),
            }
          : [...(acc[format(item, firstPath || key)] || []), item],
      };
    }, {});
  
  export function getFormattedDates(
    dates: Date[],
    ...dateFnsOptions: Required&lt;string[]&gt;
  ): ToRecord&lt;string[]&gt; | Date[] {
    return groupObject(dates, undefined, ...dateFnsOptions);
  }
  </code></pre>
  <p>I can see a pattern when I try to write it like this, but I am having a hard time making a generic recursive function that would work for all these options.</p>
  <pre><code>  return dates.reduce(
      (acc: ToRecord&lt;string[]&gt; | Record&lt;string, never&gt;, item) =&gt; {
        const item1 = format(item, dateFnsOptions[0]);
        const item2 = format(item, dateFnsOptions[1]);
        const item3 = format(item, dateFnsOptions[2]);
        // ...
        // itemN = format(item, dateFnsOptions[n]);
      
        acc[item1] = {
          ...(acc[item1] || {}),
          [item2]: {
            ...((acc[item1] || {})?.[item2] || []),
            [item3]: [
              ...(((acc[item1] || {})?.[item2] || {})?.[item3] || []),
              item,
            ],
          },
        };
  
        return acc;
      },
      {}
    );
  </code></pre>
  <p>The above function doesn't group them correctly, as after the 2nd level, it appears only the last month, and on the last level only the last day of that month.</p>
  <pre><code>const formattedDates = getFormattedDates(dates, 'yyyy', 'qqq', 'MMM', 'do');
  
  // result
  {
    2021: {Q4: {…}}
    2022:
      Q1:
        Mar:
          31st: Array(1)
      Q2:
        Jun:
          30th: Array(1)
  }
  
  </code></pre>
  <p>So the desired result should be, since now is Dec 17 2021:</p>
  <pre><code>{
    2021: {Q4: {…}}
    2022:
      Q1:
        Jan:
          1st: Array(1)
          2nd: Array(1)
          3rd: Array(1)
          ...
        Feb: {…}
        Mar: {…}
      Q2: {…}
      Q3: {…}
      Q4: {…}
  
  }
  </code></pre>
  <p>For this function, the result would be that it would group all the dates belonging to that month into an array.</p>
  <pre><code>const formattedDates = getFormattedDates(dates, 'yyyy', 'qqq', 'MMM');
  </code></pre>