Order entries by multiple matrix date field I have a section with trainings. In each training entry there's a matrix field where we add a new block with a training date, location, etc. So each training has multiple matrix blocks.
When I run an entry query and get all the matrix date fields inside of them, I can output it like this:

Training 1

October 1, 2020
October 5, 2020


Training 2

October 5, 2020
October 10, 2020


Training 3

October 7, 2020
October 10, 2020



Now I would like to group and order the trainings by date like this:

October 1, 2020

Training 1


October 5, 2020

Training 1
Training 2


October 7, 2020

Training 3


October 10, 2020

Training 2
Training 3



I'm close but can't get the dates sorted the right way. The template currently outputs:

October 1, 2020

Training 1


October 5, 2020

Training 1
Training 2


October 10, 2020

Training 2
Training 3


October 7, 2020

Training 3



The code I have right now:
{# Query entries that have training dates #}
{% set allEntries = craft.entries
.section('trainings')
.trainingDates(':notempty:')
.all() %}

{# Merge all training dates in an array #}
{% set startDates = [] %}
{% for entry in allEntries %}
    {% for training in entry.trainingDates.all() %}
        {% if training.startDate | date('d mm Y') not in startDates %}
            {% set startDates = startDates | push(training.startDate | date('d mm Y')) %}
        {% endif %}
    {% endfor %}
{% endfor %}

{# Show all trainings grouped by date #}
&lt;ul&gt;
    {% for startDate in startDates %}
        &lt;li&gt;
            &lt;h2&gt;{{ startDate }}&lt;/h2&gt;
            &lt;ul&gt;
                {% for entry in allEntries %}
                    {% for date in entry.trainingDates.all() %}
                        {% if date.startDate | date('d mm Y') == startDate %}
                            &lt;li&gt;&lt;a href=&quot;{{ entry.url }}&quot; title=&quot;{{ entry.title }}&quot;&gt;{{ entry.title }}&lt;/a&gt;&lt;/li&gt;
                        {% endif %}
                    {% endfor %}
                {% endfor %}
            &lt;/ul&gt;
        &lt;/li&gt;
    {% endfor %}
&lt;/ul&gt;

Any idea on how to sort the array by date? And this can probably done much easier. If you have any tips, I would love to hear from you! :-)
The quick and easy answer would be to just add the Twig sort filter to your startDates array. e.g:
{# Query entries that have training dates #}
{% set allEntries = craft.entries
    .section('trainings')
    .with('trainingDates')
    .trainingDates(':notempty:')
    .all()
%}

{# Merge all training dates in an array #}
{% set startDates = [] %}
{% for entry in allEntries %}
    {% for training in entry.trainingDates %}
        {% if training.startDate | date('d mm Y') not in startDates %}
            {% set startDates = startDates | push(training.startDate | date('d mm Y')) %}
        {% endif %}
    {% endfor %}
{% endfor %}

{# Show all trainings grouped by date#}
&lt;ul&gt;
    {% for startDate in startDates|sort %}
        &lt;li&gt;
            &lt;h2&gt;{{ startDate }}&lt;/h2&gt;
            &lt;ul&gt;
                {% for entry in allEntries %}
                    {% for date in entry.trainingDates %}
                        {% if date.startDate | date('d mm Y') == startDate %}
                            &lt;li&gt;&lt;a href=&quot;{{ entry.url }}&quot; title=&quot;{{ entry.title }}&quot;&gt;{{ entry.title }}&lt;/a&gt;&lt;/li&gt;
                        {% endif %}
                    {% endfor %}
                {% endfor %}
            &lt;/ul&gt;
        &lt;/li&gt;
    {% endfor %}
&lt;/ul&gt;

(I've also added eager loading to improve performance).
You could potentially attack the problem from the opposite direction by using a matrixblock element query like this:
{# Query matrix blocks that have training dates #}
{% set matrixBlocksQuery = craft.matrixBlocks().field('trainingDates') %}
{% set uniqueDatesSorted = matrixBlocksQuery.all()|map(block =&gt; block.startDate|date('d mm Y'))|unique|sort %}

{# Query entries that have training dates #}
{% set allEntries = craft.entries
    .section('trainings')
    .with('trainingDates')
    .trainingDates(':notempty:')
    .all()
%}

{# Show all trainings grouped by date#}
&lt;ul&gt;
    {% for startDate in uniqueDatesSorted %}
        &lt;li&gt;
            &lt;h2&gt;{{ startDate }}&lt;/h2&gt;
            &lt;ul&gt;
                {% for entry in allEntries %}
                    {% for date in entry.trainingDates %}
                        {% if date.startDate|date('d mm Y') == startDate %}
                            &lt;li&gt;&lt;a href=&quot;{{ entry.url }}&quot; title=&quot;{{ entry.title }}&quot;&gt;{{ date.id }}{{ entry.title }}&lt;/a&gt;&lt;/li&gt;
                        {% endif %}
                    {% endfor %}
                {% endfor %}
            &lt;/ul&gt;
        &lt;/li&gt;
    {% endfor %}
&lt;/ul&gt;

...but to be honest I think I prefer your approach, as it's more readable and doesn't require an extra .all() query to get the dates