Reputation: 4981
I'd like to remove a single key from a dictionary in Ansible.
For example, I'd like this:
- debug: var=dict2
vars:
dict:
a: 1
b: 2
c: 3
dict2: "{{ dict | filter_to_remove_key('a') }}"
To print this:
ok: [localhost] => {
"dict2": {
"b": 2,
"c": 3
}
}
Please note that the dictionary is loaded from a json file and I POST it to the Grafana REST API. I'd like to allow saving an 'id' key in the file and remove the key before POSTing it.
This is closer to the actual use I have for the removal:
- name: Install Dashboards
uri:
url: "{{ grafana_api_url }}/dashboards/db"
method: POST
headers:
Authorization: Bearer {{ grafana_api_token }}
body:
overwrite: true
dashboard:
"{{ lookup('file', item) | from_json | removekey('id') }}"
body_format: json with_fileglob:
- "dashboards/*.json"
- "../../../dashboards/*.json"
Upvotes: 20
Views: 26734
Reputation: 1
Another possible solution to this without using filter and still only deleting on the first layer (or a controllable layer depth) is using dict2items and items2dict
- set_fact:
temp:
a: 1
b:
c: 2
d:
- 3
- debug:
var: temp | dict2items | selectattr('key', 'ne', 'b') | items2dict
This will result in:
a: 1
d:
- 3
With this selectattr you could also define better checks for which keys to delete.
Upvotes: 0
Reputation: 13412
You can use the ansible.utils.remove_keys filter. It will remove the given key(s) on all nesting layers. So it might not be suitable in every scenario:
- debug: var=dict2
vars:
dict:
a: 1
b: 2
c: 3
dict2: "{{ dict | ansible.utils.remove_keys(target=['a']) }}"
Output:
ok: [localhost] =>
dict2:
b: 2
c: 3
If you want to remove a particular key on a particular layer, you can pop that key away like this:
- debug: var=dict2
vars:
dict:
a: 1
b: 2
c: 3
dict2: |
{% set a = dict.pop('a') %}
{{ dict }}
Example with deeper nesting:
- debug: var=dict2
vars:
dict:
sub_dict:
a: 1
b: 2
c: 3
dict2: |
{% set a = dict.sub_dict.pop('a') %}
{{ dict }}
Upvotes: 7
Reputation: 91
You can accomplish this by using combine and omit
- set_fact:
dict:
a: 1
b: 2
c: 3
- debug:
var: dict
- debug:
msg: "{{ dict | combine({ 'a': omit }) }}"
TASK [test : set_fact]
ok: [server]
TASK [test: debug]
ok: [server] => {
"dict": {
"a": 1,
"b": 2,
"c": 3
}
}
TASK [test : debug]
ok: [server] => {
"msg": {
"b": 2,
"c": 3
}
}
Upvotes: 9
Reputation: 61
- debug: var=dict2
vars:
dict:
a: 1
b: 2
c: 3
dict2: '{{ dict | dict2items | rejectattr("key", "eq", "a") | list | items2dict }}'
#dict2: '{{ dict | dict2items | rejectattr("key", "match", "^(a|b)$") | list | items2dict }}'
Output:
ok: [localhost] => {
"dict2": {
"b": 2,
"c": 3
}
}
Upvotes: 6
Reputation: 23
If you interested in filter (which is in my opinion the cleanest way to delete an item in dict) then create filter_plugins/dicts.py
in directory, which your playbook resides in, and fill it with:
'''Custom ansible filters for dicts'''
import copy
class FilterModule(object):
def filters(self):
return {
'del_by_list': self.del_by_list
}
def del_by_list(self, dict_to_reduce, list_of_keys):
'''Deletes items of dict by list of keys provided'''
dict_to_return = copy.deepcopy(dict_to_reduce)
for item in list_of_keys:
if item in dict_to_return:
del dict_to_return[item]
return dict_to_return
And you good to go:
---
- hosts: hadoop
gather_facts: no
tasks:
- debug:
msg: "{{ {123: 456, 789: 123} | del_by_list([123]) }}"
This will yield {789: 123}
Upvotes: 2
Reputation: 4102
Here's an approach inspired by an article by John Mazzitelli that can be used inline without additional set_fact
tasks, etc.:
tasks:
- debug: var=dict2
vars:
dict:
a: 1
b: 2
c: 3
# It is important that there be NO WHITESPACE outside of `{% ... %}` and `{{ ... }}`
# or else the var will be converted to a string. The copy() step optionally prevents
# modifying the original. If you don't care, then: "{% set removed=dict.pop('a') %}{{dict}}"
dict2: "{% set copy=dict.copy() %}{% set removed=copy.pop('a') %}{{ copy }}"
TASK [debug] ***********
ok: [localhost] => {
"dict2": {
"b": 2,
"c": 3
}
}
Upvotes: 5
Reputation: 367
- set_fact:
dict:
a: 1
b: 2
c: 3
dict2: {}
- set_fact:
dict2: "{{dict2 |combine({item.key: item.value})}}"
when: "{{item.key not in ['a']}}"
with_dict: "{{dict}}"
- debug: var=dict2
or create a filter plugin and use it.
Upvotes: 14