Reputation: 4901
I have a Jinja template with a list of dictionaries. Order matters. I'd like to reduce the list or lookup values based on the keys/values of the dictionaries. Here's an example:
{%
set ordered_dicts = [
{
'id': 'foo',
'name': 'My name is Foo'
},
{
'id': 'bar',
'name': 'My name is Bar'
}
]
%}
If I have a variable some_id = 'foo'
, how do I get 'My name is Foo'
out of ordered_dicts
in my Jinja template?
I tried select()
and selectattr()
but couldn't figure them out based on the documentation. Here's what I tried:
{{ ordered_dicts|selectattr("id", "foo") }}
That outputs:
<generator object _select_or_reject at 0x10748d870>
I don't think I'm understanding the use of select()
and selectattr()
properly.
Do I need to iterate over the list and do the lookup manually?
Update:
As codegeek and gipi pointed out, I need to do something like this with the generator:
{{ ordered_dicts|selectattr("id", "foo")|list }}
The resulting error: TemplateRuntimeError: no test named 'foo'
, which clarifies how selectattr()
works. The second argument has to be one of the builtin tests. As far as I can tell, none of these tests will let me check whether the value associated with a key matches another value. Here's what I'd like to do:
{{ ordered_dicts|selectattr("id", "sameas", "foo")|list }}
But that doesn't work, since the sameas
test checks whether two objects are really the same object in memory, not whether two strings/numbers are equivalent.
So is it possible to pick an item based on a key/value comparison test?
Upvotes: 25
Views: 51454
Reputation: 2515
select()
and selectattr()
act upon a list
and return a list
, so if you know that there is only one result take the first from the generator, i.e
{{ oredered_dicts|selectattr("id", "foo")|first }}
Note: code not tested
Upvotes: 4
Reputation: 627
For people who don't have selectattr (e.g. you're stuck with Jinja2.6), and don't want to make yet another custom filter, these 2 lines will solve your problem real quick.
{% set selection = [] %}
{% for x in biglist if x.criteria == 'pickme' %}{% do selection.append(x) %}{% endfor %}
Upvotes: 7
Reputation: 111
More natural way for Ansible is to create /etc/ansible/test_plugins/custom.py with content
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
from ansible import errors
def equalto(value, other):
return bool(value == other)
class TestModule(object):
''' Ansible file jinja2 tests '''
def tests(self):
return {
'equalto' : equalto,
}
Upvotes: 1
Reputation: 3818
Have a look to https://github.com/ansible/ansible/issues/8836 for this issue.
A solution/workaround is to create a file filter_plugins/core.py in your workbook directory with the following content:
def filter_list(list, key, value):
return filter(lambda t: t[key] == value, list)
class FilterModule(object):
def filters(self):
return {
'byattr': filter_list
}
And use it so:
{{ ordered_dicts|byattr("id", "foo") }}
Upvotes: 2
Reputation: 9528
I've just backported equalto
like this:
app.jinja_env.tests['equalto'] = lambda value, other : value == other
After that this example from 2.8 docs works:
{{ users|selectattr("email", "equalto", "[email protected]") }}
Update: Flask has a decorator for registering tests, slightly cleaner syntax: http://flask.pocoo.org/docs/api/#flask.Flask.template_test
Upvotes: 24
Reputation: 7344
It looks like the equalto filter is coming in Jinja2.8 (changelog) but it doesn't have any release date set yet (24 Feb 2014). As a workaround I'd suggest using groupby filter:
<ul>
{% for group in persons|groupby('gender') %}
<li>{{ group.grouper }}<ul>
{% for person in group.list %}
<li>{{ person.first_name }} {{ person.last_name }}</li>
{% endfor %}</ul></li>
{% endfor %}
</ul>
Upvotes: 0