<p>You could order the dates inside the for-loop, but I would order the dates before they're grouped, much simpler. You can do this in one clean pipeline, much easier to reason about and understand:</p>
<pre class="lang-twig prettyprint-override"><code>{% set datesByMonth = entry.dates
    |filter(date =&gt; date.start &gt;= now)
    |sort((a, b) =&gt; a.start &lt;=&gt; b.start)
    |group(date =&gt; date.start | date('F Y'))
%}
</code></pre>
<p>First, the <code>filter</code> removes all dates before today, this means you can get rid of the first for-loop. Then, the array is sorted using the <code>sort</code> filter. Finally, the sorted array is grouped using the <code>group</code> filter. Since the array was already sorted, the grouped items will be sorted as well.</p>
<p>This assumes that <code>entry.dates</code> returns an array of <code>DateTime</code> objects. You can compare <code>DateTime</code> objects directly (both in PHP and Twig), so you don't need the <code>date</code> filter. If <code>entry.dates</code> is an array of something else (like dates string in a specific format), you can first map that to an array of <code>DateTime</code> objects using the <a href="https://twig.symfony.com/doc/3.x/filters/map.html" rel="nofollow noreferrer"><code>map</code> filter</a> and the <a href="https://craftcms.com/docs/4.x/dev/functions.html#date" rel="nofollow noreferrer"><code>date</code> function</a>:</p>
<pre class="lang-twig prettyprint-override"><code>{% set datesByMonth = entry.dates
    |map(dateString =&gt; date(dateString))
    |filter(date =&gt; date.start &gt;= now)
    |sort((a, b) =&gt; a.start &lt;=&gt; b.start)
    |group(date =&gt; date.start | date('F Y'))
%}
</code></pre>

CC BY-SA 4.0
2023-09-27T23:36:31.730Z
That's nearly there except that on the page I'm testing, there are events in September, October, November, but the first heading that appears on the page is October 2023.
Tyssen
CC BY-SA 4.0
2023-09-27T23:54:06.290Z
Actually, not looking at the dates closely enough, there are dates in September, but none after today, so all good, thanks. :)
Tyssen
CC BY-SA 4.0
2023-09-28T11:07:45.703Z
@Tyssen If the order is really inverted, just switch the `a` and `b`, I always get the order wrong the first time :-)
MoritzLost
CC BY-SA 4.0
2023-09-27T08:57:33.343Z
2023-09-27T08:57:33.343Z
MoritzLost
<p>I need to be able to group dates by their month but then order them by month too because the order they're inserted into the database in na super field table isn't always in date order.</p>
<p>So far, I have this:</p>
<pre><code>{% set today = now|date('U') %}
{% set dates = [] %}
{% for date in entry.dates %}
    {% if date.start|date('U') &gt;= today %}
    {% set dates = dates|merge([date]) %}
    {% endif %}
{% endfor %}

{% set datesByMonth = dates|group('start|date(&quot;F Y&quot;)') %}
{% for month, dates in datesByMonth %}
    {{ month }}
    dates stuff
{% endfor %}
</code></pre>
<p>which works fine except for the ordering.</p>
<p>I thought for the ordering, I might be able to do this:</p>
<pre><code>{% for month, dates in datesByMonth|multisort(['start|date(&quot;c&quot;)']) %}
…
{% endfor %}
</code></pre>
<p>but that doesn't change the order. Am I doing that wrong or is there another way?</p>

CC BY-SA 4.0
2023-09-27T05:47:32.233Z
What's in your `dates` variable - entries, `DateTime` values?
MoritzLost
CC BY-SA 4.0
2023-09-27T05:48:43.447Z
If it's entries, please post the entry query as well, might be possible to order the results right in the query.
MoritzLost
CC BY-SA 4.0
2023-09-27T05:51:33.627Z
@MoritzLost it's an array from a super table field which contains datetime start and end columns.
Tyssen
CC BY-SA 4.0
2023-09-27T05:54:42.413Z
@MoritzLost I've edited the question to include more of the code prior to what I originally posted.
Tyssen
<datetime><group>
Grouping and ordering entries by month
# Grouping and ordering entries by month

(Asked by: username_0 on 2023-09-27)

I need to be able to group dates by their month but then order them by month too because the order they're inserted into the database in na super field table isn't always in date order.
So far, I have this:
{% set today = now|date('U') %}
{% set dates = [] %}
{% for date in entry.dates %}
    {% if date.start|date('U') >= today %}
    {% set dates = dates|merge([date]) %}
    {% endif %}
{% endfor %}

{% set datesByMonth = dates|group('start|date("F Y")') %}
{% for month, dates in datesByMonth %}
    {{ month }}
    dates stuff
{% endfor %}

which works fine except for the ordering.
I thought for the ordering, I might be able to do this:
{% for month, dates in datesByMonth|multisort(['start|date("c")']) %}
…
{% endfor %}

but that doesn't change the order. Am I doing that wrong or is there another way?

**Tags:** datetime, group

> **Comments:**
> **username_1**: What's in your `dates` variable - entries, `DateTime` values?  
> **username_1**: If it's entries, please post the entry query as well, might be possible to order the results right in the query.  
> **username_0**: @username_1 it's an array from a super table field which contains datetime start and end columns.  
> **username_0**: @username_1 I've edited the question to include more of the code prior to what I originally posted.  

---

## 1 answers

### Answer by username_1 on 2023-09-27. Score: 1 | Accepted answer

You could order the dates inside the for-loop, but I would order the dates before they're grouped, much simpler. You can do this in one clean pipeline, much easier to reason about and understand:
{% set datesByMonth = entry.dates
    |filter(date => date.start >= now)
    |sort((a, b) => a.start <=> b.start)
    |group(date => date.start | date('F Y'))
%}

First, the filter removes all dates before today, this means you can get rid of the first for-loop. Then, the array is sorted using the sort filter. Finally, the sorted array is grouped using the group filter. Since the array was already sorted, the grouped items will be sorted as well.
This assumes that entry.dates returns an array of DateTime objects. You can compare DateTime objects directly (both in PHP and Twig), so you don't need the date filter. If entry.dates is an array of something else (like dates string in a specific format), you can first map that to an array of DateTime objects using the map filter and the date function:
{% set datesByMonth = entry.dates
    |map(dateString => date(dateString))
    |filter(date => date.start >= now)
    |sort((a, b) => a.start <=> b.start)
    |group(date => date.start | date('F Y'))
%}

> **Comments:**
> **username_0**: That's nearly there except that on the page I'm testing, there are events in September, October, November, but the first heading that appears on the page is October 2023.  
> **username_0**: Actually, not looking at the dates closely enough, there are dates in September, but none after today, so all good, thanks. :)  
> **username_1**: @username_0 If the order is really inverted, just switch the `a` and `b`, I always get the order wrong the first time :-)  

---

