Sort dict of dict in jinja2 loop
Asked Answered
S

4

12

How can I sort a dict like

my_dict = {
    'abc': {'name': 'B', 'is_sth': True},
    'xyz': {'name': 'A', 'is_sth': True}
}

by name in Jinja?

I've tried {% for id, data in my_dict|dictsort(by='value') if data.is_sth %} but doesn't work as I expect.

Scandal answered 21/4, 2017 at 11:45 Comment(2)
what do you expect?Thetis
@Jean-FrançoisFabre he expects {'xyz': {'name': 'A', 'is_sth': True}, 'abc': {'name': 'B', 'is_sth': True}}Angelikaangelina
S
28

Solution:

my_dict.items()|sort(attribute='1.name')

Scandal answered 26/4, 2017 at 15:1 Comment(5)
Some explanation would help, I understand "name" but what does the "1." relate to?Sprang
.items() returns a tuple of two elements: key and value. 1 relates to the 2nd element i.e. the value and .name relates to the key from the internal dict.Scandal
this answer proposes you don't need the 'items()' call: https://mcmap.net/q/179665/-how-do-you-sort-a-list-in-jinja2Ot
So i understand i can reference in this way any inner key, does not matter the complexity of nesting ? like 1.name.x.y.z ?Parade
@Ot you do need it if you are unpacking the key, value pairs as the question's code does. In the answer you're linking they get them as a single variable.Hypothesis
G
6

jinja2 has a perfect solution for it

do_dictsort(value, case_sensitive=False, by='key')

{% for item in mydict|dictsort %}
    sort the dict by key, case insensitive

{% for item in mydict|dicsort(true) %}
    sort the dict by key, case sensitive

{% for item in mydict|dictsort(false, 'value') %}
    sort the dict by key, case insensitive, sorted
    normally and ordered by value.
Garnett answered 24/4, 2020 at 18:57 Comment(0)
A
0

if you can live with ordering your data outside jinja and then just displaying it, you could do this:

from jinja2 import Template
from collections import OrderedDict

tmplt = Template('''
{% for id, data in od.items() if data.is_sth %}
{{id}}, {{data}}
{% endfor %}
''')

od = OrderedDict((key, value) for key, value in
                 sorted(my_dict.items(), key=lambda x: x[1]['name']))

print(tmplt.render(od = od))

which gives:

xyz, {'name': 'A', 'is_sth': True}

abc, {'name': 'B', 'is_sth': True}

other than that you'd probably have to create a custom filter.

Allottee answered 21/4, 2017 at 12:10 Comment(3)
That would work, but I would rather to order it in jinja. Actually I think I've found a solution: {% for id, data in my_dict.items()|sort(attribute='1.name') if data.is_sth %}Scandal
@Scandal ha! i was trying something along those lines but did not the the quotes around '1.name' right... you can post what you found here as an answer yourself! (but only 24h after the question if i remember correctly). good luck! (so it's my_dict.items()|sort(attribute='1.name'), right?)Allottee
oh, sorry about the last question; you had the jinja statement in your comment already...Allottee
H
0

Just for completeness...

Jinja has (at least?) two different ways of sorting the dictionaries. One is dictsort and the other is sort.

dictsort

{% for key, value in dictionary|dictsort %}

This way of doing it gives you the key, value pairs directly, without .items(), and sorts the dictionaries by key.

sort

{% for key, value in dictionary.items()|sort(attribute="sort_by_?") %}

This way of doing it needs you to ask for the .items(), just like standard python would. But, it lets you specify a somewhat custom way to sort them.

Within the attribute string (it needs to be a string) you can use 0 to reference the dictionary key and 1 to reference the dictionary value. If you want a specific inner key that exists inside of that value, you can access it by a . (examples below).

Also, you can use jinja's | stuff inside the attribute string if you need to convert strings to integers or whatever (it goes inside of the string).

sort examples

Sorting by value["name"]

{% for key, value in dictionary.items()|sort(attribute="1.name")%}

Sorting by key (but the stupid way, since this would be the default if you don't specify any attribute)

{% for key, value in dictionary.items()|sort(attribute="0")%}

Sorting by int(key) so that "10" doesn't end up before "2" when you have numbers stored as strings.

{% for key, value in dictionary.items()|sort(attribute="0|int")%}
Hypothesis answered 12/1 at 11:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.