From aea396f04bfa40aee231b277e144b198128a75c7 Mon Sep 17 00:00:00 2001 From: Matt Martz Date: Fri, 29 Jun 2018 10:47:07 -0500 Subject: [PATCH] Add items2dict filter that is the reverse of dict2items (#42071) * Add items2dict filter that is the reverse of dict2items * Address feedback about type checking, and add docs for zip/zip_longest --- .../rst/user_guide/playbooks_filters.rst | 88 +++++++++++++++---- lib/ansible/plugins/filter/core.py | 12 +++ 2 files changed, 84 insertions(+), 16 deletions(-) diff --git a/docs/docsite/rst/user_guide/playbooks_filters.rst b/docs/docsite/rst/user_guide/playbooks_filters.rst index 8c0a7404359..de87d35883a 100644 --- a/docs/docsite/rst/user_guide/playbooks_filters.rst +++ b/docs/docsite/rst/user_guide/playbooks_filters.rst @@ -184,6 +184,77 @@ into:: - key: Environment value: dev +items2dict filter +````````````````` + +.. versionadded:: 2.7 + +This filter turns a list of dicts with 2 keys, into a dict, mapping the values of those keys into ``key: value`` pairs:: + + {{ tags | items2dict }} + +Which turns:: + + tags: + - key: Application + value: payment + - key: Environment + value: dev + +into:: + + Application: payment + Environment: dev + +This is the reverse of the ``dict2items`` filter. + +``items2dict`` accepts 2 keyword arguments, ``key_name`` and ``value_name`` that allow configuration of the names of the keys to use for the transformation:: + + {{ tags | items2dict(key_name='key', value_name='value') }} + + +.. _zip_filter: + +zip and zip_longest filters +``````````````````````````` + +.. versionadded:: 2.3 + +To get a list combining the elements of other lists use ``zip``:: + + - name: give me list combo of two lists + debug: + msg: "{{ [1,2,3,4,5]|zip(['a','b','c','d','e','f'])|list }}" + + - name: give me shortest combo of two lists + debug: + msg: "{{ [1,2,3]|zip(['a','b','c','d','e','f'])|list }}" + +To always exhaust all list use ``zip_longest``:: + + - name: give me longest combo of three lists , fill with X + debug: + msg: "{{ [1,2,3]|zip_longest(['a','b','c','d','e','f'], [21, 22, 23], fillvalue='X')|list }}" + + +Similarly to the output of the ``items2dict`` filter mentioned above, these filters can be used to contruct a ``dict``:: + + {{ dict(keys_list | zip(values_list)) }} + +Which turns:: + + list_one: + - one + - two + list_two: + - apple + - orange + +into:: + + one: apple + two: orange + subelements Filter `````````````````` @@ -1074,22 +1145,7 @@ Combinations always require a set size:: msg: "{{ [1,2,3,4,5]|combinations(2)|list }}" -To get a list combining the elements of other lists use ``zip``:: - - - name: give me list combo of two lists - debug: - msg: "{{ [1,2,3,4,5]|zip(['a','b','c','d','e','f'])|list }}" - - - name: give me shortest combo of two lists - debug: - msg: "{{ [1,2,3]|zip(['a','b','c','d','e','f'])|list }}" - -To always exhaust all list use ``zip_longest``:: - - - name: give me longest combo of three lists , fill with X - debug: - msg: "{{ [1,2,3]|zip_longest(['a','b','c','d','e','f'], [21, 22, 23], fillvalue='X')|list }}" - +Also see the :ref:`zip_filter` .. versionadded:: 2.4 diff --git a/lib/ansible/plugins/filter/core.py b/lib/ansible/plugins/filter/core.py index a46f0aef615..1e533b52c26 100644 --- a/lib/ansible/plugins/filter/core.py +++ b/lib/ansible/plugins/filter/core.py @@ -51,6 +51,7 @@ from ansible.errors import AnsibleFilterError from ansible.module_utils.six import iteritems, string_types, integer_types from ansible.module_utils.six.moves import reduce, shlex_quote from ansible.module_utils._text import to_bytes, to_text +from ansible.module_utils.common.collections import is_sequence from ansible.parsing.ajson import AnsibleJSONEncoder from ansible.parsing.yaml.dumper import AnsibleDumper from ansible.utils.hashing import md5s, checksum_s @@ -532,6 +533,16 @@ def dict_to_list_of_dict_key_value_elements(mydict): return ret +def list_of_dict_key_value_elements_to_dict(mylist, key_name='key', value_name='value'): + ''' takes a list of dicts with each having a 'key' and 'value' keys, and transforms the list into a dictionary, + effectively as the reverse of dict2items ''' + + if not is_sequence(mylist): + raise AnsibleFilterError("items2dict requires a list, got %s instead." % type(mylist)) + + return dict((item[key_name], item[value_name]) for item in mylist) + + def random_mac(value): ''' takes string prefix, and return it completed with random bytes to get a complete 6 bytes MAC address ''' @@ -653,6 +664,7 @@ class FilterModule(object): 'extract': extract, 'flatten': flatten, 'dict2items': dict_to_list_of_dict_key_value_elements, + 'items2dict': list_of_dict_key_value_elements_to_dict, 'subelements': subelements, # Misc