1
0
Fork 0
mirror of https://github.com/ansible-collections/community.general.git synced 2024-09-14 20:13:21 +02:00

Add groupby_as_dict filter (#2323)

* Add groupby_as_dict filter.

* Test all error cases.
This commit is contained in:
Felix Fontein 2021-05-13 21:49:57 +02:00 committed by GitHub
parent 23dda56913
commit 384655e15c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 123 additions and 0 deletions

View file

@ -0,0 +1,3 @@
add plugin.filter:
- name: groupby_as_dict
description: Transform a sequence of dictionaries to a dictionary where the dictionaries are indexed by an attribute

42
plugins/filter/groupby.py Normal file
View file

@ -0,0 +1,42 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2021, Felix Fontein <felix@fontein.de>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
from ansible.errors import AnsibleFilterError
from ansible.module_utils.common._collections_compat import Mapping, Sequence
def groupby_as_dict(sequence, attribute):
'''
Given a sequence of dictionaries and an attribute name, returns a dictionary mapping
the value of this attribute to the dictionary.
If multiple dictionaries in the sequence have the same value for this attribute,
the filter will fail.
'''
if not isinstance(sequence, Sequence):
raise AnsibleFilterError('Input is not a sequence')
result = dict()
for list_index, element in enumerate(sequence):
if not isinstance(element, Mapping):
raise AnsibleFilterError('Sequence element #{0} is not a mapping'.format(list_index))
if attribute not in element:
raise AnsibleFilterError('Attribute not contained in element #{0} of sequence'.format(list_index))
result_index = element[attribute]
if result_index in result:
raise AnsibleFilterError('Multiple sequence entries have attribute value {0!r}'.format(result_index))
result[result_index] = element
return result
class FilterModule(object):
''' Ansible list filters '''
def filters(self):
return {
'groupby_as_dict': groupby_as_dict,
}

View file

@ -0,0 +1,2 @@
shippable/posix/group4
skip/python2.6 # filters are controller only, and we no longer support Python 2.6 on the controller

View file

@ -0,0 +1,45 @@
---
- name: Test functionality
assert:
that:
- list1 | community.general.groupby_as_dict('name') == dict1
- name: 'Test error: not a list'
set_fact:
test: "{{ list_no_list | community.general.groupby_as_dict('name') }}"
ignore_errors: true
register: result
- assert:
that:
- result.msg == 'Input is not a sequence'
- name: 'Test error: list element not a mapping'
set_fact:
test: "{{ list_no_dict | community.general.groupby_as_dict('name') }}"
ignore_errors: true
register: result
- assert:
that:
- "result.msg == 'Sequence element #0 is not a mapping'"
- name: 'Test error: list element does not have attribute'
set_fact:
test: "{{ list_no_attribute | community.general.groupby_as_dict('name') }}"
ignore_errors: true
register: result
- assert:
that:
- "result.msg == 'Attribute not contained in element #1 of sequence'"
- name: 'Test error: attribute collision'
set_fact:
test: "{{ list_collision | community.general.groupby_as_dict('name') }}"
ignore_errors: true
register: result
- assert:
that:
- result.msg == "Multiple sequence entries have attribute value 'a'"

View file

@ -0,0 +1,31 @@
---
list1:
- name: a
x: y
- name: b
z: 1
dict1:
a:
name: a
x: y
b:
name: b
z: 1
list_no_list:
a:
name: a
list_no_dict:
- []
- 1
list_no_attribute:
- name: a
foo: baz
- foo: bar
list_collision:
- name: a
- name: a