How to reformat a yaml dictionary?

Hello,

So I have a var dictionary which stores component groups and components

components_by_group:
web:

  • frontend_app
  • admin_app
    database:
  • db_tools
  • flyway

I’d like to invert this so that I can use the component to lookup the group

group_by_component:
frontend_app: web
admin_app: web
db_tools: database
flyway: database

I can make a list of the groups easily enough…

  • name: make a list of the groups
    set_fact:
    component_groups: “{{ component_groups|default() + [ item.key ] }}”
    with_dict: “{{ components_by_group }}”

How can I make a dict that looks like group_by_component above?

Many thanks,

Jon

I’m still bugged I can’t figure this out.

I can make a list of all components easily enough too

  • name: make a list of the components
    set_fact:
    components: “{{ components|default() |union( item.value) }}”
    with_dict: “{{ components_by_group }}”

I haven’t figured a way to create more output key=value pairs than their are input items, so it would likely be useful to use the list of components in a with_items. But then I am stuck trying to lookup the group that the component belongs to.

If you could pass a list as the second arg to with_subelements that would let you loop through all the components in each of the groups and add them to an output, but with_subelements only takes a string for its second arg.

I even wondered about using with_cartesian on the components and groups lists and only adding to the output when group and component match components_by_group but I think you just get a single flat list with the cartesian product in it.

Any suggestions - devious or elegant - would be gratefully appreciated.

Jon

I couldnt get it to work with you variable. It is just bad formatted.

If you could format it like this it would be easy:

components_by_group:

  • name: web
    components:
  • frontend_app
  • admin_app
  • name: database
    components:
  • db_tools
  • flyway

tasks:

  • name: Change the format of the Group
    set_fact:
    group_by_components: “{{ group_by_components | default(dict()) | combine({ item.1 : item.0.name }) }}”
    with_subelements:
  • “{{ components_by_group }}”
  • components

Outputs:

ok: [localhost] => {
“group_by_components”: {
“admin_app”: “web”,
“db_tools”: “database”,
“flyway”: “database”,
“frontend_app”: “web”
}
}

Thank you for this. It might well be easier for me to reformat the source variable.

The thing I want to avoid most of all is having to maintain two separate variables so the ability to transform from one to another at playbook runtime is the thing I need the most.

Jon

Hi,

If you feel up to it - write a filter plugin. They’re quite easy to create. Have a look here for an example: http://www.dasblinkenlichten.com/creating-ansible-filter-plugins/
I use them when I have limited control over the data structure I have to deal with. Even if data manipulation can be done in ‘pure’ Ansible it ends up being quite convoluted (and usually spread across multiple files, since ‘include’ is the only way to have loop inside a loop whilst retaining full control over the data).

kind regards
Pshem

Lol, how cool is that!!! I never heard about it :open_mouth:

Thanks that’s a good idea. I’m struggling to come up with a good name for the filter though. I’ll think about it a bit more but best place to start almost seems to be extending with_subelements so the second parameter can be a list (which would at least mean I don’t have to come up with a new filter name. I guess I could make something for internal use only, but would rather create something that I could contribute back to ansible.

Jon