In a nutshell, you can get your result with a single expression without any loops.
The following playbook:
---
- hosts: localhost
gather_facts: false
vars:
# Your original data on a single line for legibility
result_clusters:
stdout: >-
{"clusters":{"Cluster_1":{"hosts":[{"folder":"/path","name":"host1.domain.com"},{"folder":"/path","name":"host2.domain.com"}]},"Cluster_2":{"hosts":[{"folder":"/path","name":"host3.domain.com"},{"folder":"/path","name":"host4.domain.com"}]}}}
tasks:
- name: get my expected output from json data in a single task
vars:
query: >-
[].{name: key, hosts: join(', ', value.hosts[].name)}
clusters: "{{ (result_clusters.stdout | from_json).clusters }}"
debug:
msg: "{{ clusters | dict2items | json_query(query) }}"
Gives:
PLAY [localhost] ******************************************************
TASK [get my expected output from json data in a single task] *********
ok: [localhost] => {
"msg": [
{
"hosts": "host1.domain.com, host2.domain.com",
"name": "Cluster_1"
},
{
"hosts": "host3.domain.com, host4.domain.com",
"name": "Cluster_2"
}
]
}
PLAY RECAP ************************************************************
localhost: ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Note: although the above gives very precisely the output you asked for, I strongly suspect you are looking for a result slightly different. Replacing the above query with:
query: >-
[].{name: key, hosts: value.hosts[].name}
will give this instead:
TASK [get my expected output from json data in a single task] **************************************************************************************************************************************************************************
ok: [localhost] => {
"msg": [
{
"hosts": [
"host1.domain.com",
"host2.domain.com"
],
"name": "Cluster_1"
},
{
"hosts": [
"host3.domain.com",
"host4.domain.com"
],
"name": "Cluster_2"
}
]
}