I have a dict like this
{"product_1": [
"account1",
"account2"
],
"product_2": [
"account1",
"account2"
],
...
"product_10": [
"account1",
"account2"
]}
How to filter this dict based on the key?
There are specific jinja2 filters for that: selectattr and rejectattr.
But those work on a list of dicts, not on a dict itself. You will have to use dict2items and items2dict to work around that.
Here are some example usage:
# Values are dicts, select only items with specific attribute defined.
result: "{{ my_dict | dict2items | selectattr('value.my_attribute', 'defined')
| list | items2dict }}"
# Values are lists (as in your example), check for an existing item value.
result: "{{ my_dict | dict2items | selectattr('value', 'contains', 'some item')
| list | items2dict }}"
# Values are dict. Reject elements when attributes has a specific value.
result: "{{ my_dict | dict2items | rejectattr('value.my_attribute', '==', 'Does not apply')
| list | items2dict }}"
Check the above documentations and the rest of the filters for an exhaustive view of all the possibilities.
you could use dict2items to reformat the elements to have the names you are searching for as keys, then pass it to map filter to get those keys:
---
- hosts: localhost
gather_facts: false
vars:
myvar:
product_1:
- account1
- account2
product_2:
- account1
- account2
product_10:
- account1
- account2
tasks:
- name: extract the list of keys
debug:
msg: '{{ myvar | dict2items | map(attribute="key") | list }}'
cheers
Related
Variable mule_runtimes has a list of dictionaries:
- id: N-Newton
version: 4.3.0
- id: N-Galileo
version: 3.9.0-hf4
- id: N-Einstein
version: 3.8.5-hf4
I want the dictionary with id = N-Einstein.
I have tried using this:
- debug:
msg: "{{ mule_runtimes | selectattr('id', 'equalto', 'N-Einstein') | to_json }}"
And got error: Unexpected templating type error occurred on ({{ mule_runtimes | selectattr('id', 'equalto', 'N-Einstein') | to_json }}): Object of type 'generator' is not JSON serializable.
What's the correct way to pick a dictionary from a list?
The first problem is that mule_runtimes | selectattr('id', 'equalto', 'N-Einstein') returns a generator. Think of it like d for d in mule_runtimes if d['id'] == 'N-Einstein' in Python. You'll need to convert it to something JSON serializable (like a list) before using the to_json filter.
The second problem is that it doesn't select only a single dictionary from the list. The predicate id == 'N-Einstein' could be true for multiple dictionaries. If you know it will only match one dictionary, you'll need to convert the list to a single dictionary.
Putting that all together:
{{ mule_runtimes | selectattr('id', 'equalto', 'N-Einstein') | list | last | to_json }}
I suggest json_query:
- name: dictionaries
vars:
mule_runtimes:
- id: N-Newton
version: 4.3.0
- id: N-Galileo
version: 3.9.0-hf4
- id: N-Einstein
version: 3.8.5-hf4
json: "{{ mule_runtimes }}"
query: "[?id=='{{ want }}'].version"
want: N-Einstein
debug:
msg: "{{ json | json_query(query) }}"
Gives the output:
TASK [test : dictionaries] *****************************************************
ok: [127.0.0.1] => {
"msg": [
"3.8.5-hf4"
]
}
i have ansible-playbook that gives list of lines in debug output.
I am able to filter debug OUTPUT using a string (exp: CUST) but I am struggling to filter the list using a variable.
- debug:
msg: "{{ List.msg | select('match', '^(CUST)[0-9]+') | list }}"
List msg output:
CUST1
CUST2
NEW1
NEW2
from the above debug command, i get CUST1, CUST2 in filtered output.
- set_fact:
filter: "{{ fileout.results[0].content }}"
above filter generates "CUST" and i want to use this filter variable in above debug command.
using below syntax i get nothing, may be ansible is NOT taking it as appropriate variable.
- debug:
msg: "{{ List.msg | select('match', '^("{{ filter }}")[0-9]+') | list }}"
Please help.
thanks in advance.
It's possible to isolate the declaration of the regex filter and simplify the quotation. For example
vars:
List:
msg: ['CUST1','CUST2','NEW1','NEW2']
Patterns: ['CUST','NEW']
tasks:
- debug:
msg: "{{ List.msg | select('match', filter) | list }}"
vars:
filter: '^{{ item }}[0-9]+'
loop: "{{ Patterns }}"
gives
"msg": [
"CUST1",
"CUST2"
]
"msg": [
"NEW1",
"NEW2"
]
I need to filter a dictionary in ansible based on regex matching it's values.
mydict:
some_value: /my/path
another_value: 2345
anotherpath: /my/other/path
What I tried is:
set_fact:
newvar: "{{ mydict | dict2items | selectattr('value', 'match', '^/my/.+$') | list }}"
But what i get is:
expected string or buffer
How can I filter all values based on part of their string for using them later in with_items?
The issue you currently have is that the regex match cannot handle integers. This will solve your issue, though it's slightly more verbose:
- set_fact:
file_list: []
- set_fact:
file_list: "{{ file_list + [item.value] }}"
when: item.value|string | match('^/my/.+$')
loop: "{{ mydict | dict2items }}"
If you can ensure that all values will always be strings, then you could use:
- set_fact:
newvar: "{{ mydict | dict2items | selectattr('value', 'match', '^/my/.+$') | map(attribute='value') | list }}"
Thanks #matt-p for clarifying :-) I didn't know this was actually caused by the match function itself. Is this more like an issue I should report to the Ansible community?
BTW I changed your code a little bit to fit the actual Ansible standard (the when condition is using deprecated syntax):
- set_fact:
file_list: []
- set_fact:
file_list: "{{ file_list + [item.value] }}"
when:
- item.value is string
- item.value is match('^/my/.+$')
with_items: "{{ mydict | dict2items }}"
Suppose I have the following vars_file:
mappings:
- primary: 1.1.1.1
secondary: 2.2.2.2
- primary: 12.12.12.12
secondary: 11.11.11.11
and hosts file
1.1.1.1
12.12.12.12
5.5.5.5
and the following playbook task
- name: Extract secondary from list
debug:
msg: "{{ (mappings | selectattr('primary', 'search', inventory_hostname) | list | first | default({'secondary':None})).secondary }}"
The current task works and will give empty string when no match are found, but I would like to know if there is a better way/cleaner way of doing it without passing a dictionary to the default constructor.
An option would be to use json_query
- debug:
msg: "{{ mappings | json_query(\"[?primary=='\" + inventory_hostname + \"'].secondary\") }}"
, but selectattr works too
- debug:
msg: "{{ mappings | selectattr('primary', 'equalto', inventory_hostname) | map(attribute='secondary') | list }}"
I need to extract a subset of a dictionary based on a pattern on the key names.
For example, in the v below I need to extract the key->values section1*.
The code below assigns the list of values, but I still haven't found a way to preserve the key->map setting.
- set_fact:
v:
section1_1: true
section1_2: false
section2_1: true
section2_2: false
section3: true
- set_fact:
v2: "{{ v | select('match','^section1_.*') | map('extract', v) | list }}"
- debug:
var: v2
Any help, please?
Thanks.
Combine dict2items and items2dict filters:
- debug:
msg: "{{ v | dict2items | selectattr('key', 'match', '^section1') | list | items2dict }}"
fatal: [localhost]: FAILED! => {"msg": "template error while templating string: no filter named 'items2dict'. String: {{ v | dict2items | selectattr('key', 'match', '^section1_') | list | items2dict }}"}
If I look at the sources in /usr/lib/python2.7/dist-packages I see that there are references to it, but not real function definitions
ansible/plugins/filter/core.py: raise AnsibleFilterError("items2dict requires a list, got %s instead." % type(mylist))
ansible/plugins/filter/core.py: 'items2dict': list_of_dict_key_value_elements_to_dict,
I'm runing 2.5.1. Do I need a later version?