I have a static variable that holds IP addresses of all hosts in my inventory (how to obtain this dynamically is a separate question) like this:
server_ips:
  www1.example.com:
    ipv4:
      - 192.168.0.10
      - 192.168.0.11
    ipv6:
      - '2a00:abcd:1234::100'
      - '2a00:abcd:1234::101'
  www2.example.com:
    ipv4:
    ipv6:
      - '2a00:abcd:1234::200'
      - '2a00:abcd:1234::201'
  db1.example.com:
    ipv4:
      - 192.168.1.2
    ipv6:
These names align with hosts in my inventory:
[webservers]
www1.example.com
www2.example.com
[dbservers]
db1.example.com
On a task that's run on the dbservers group, I need a list of all IPs from the webserver group (this makes querying facts directly tricky as facts may not have been gathered for those hosts) - in this case it would need to extract:
- 192.168.0.10
- 192.168.0.11
- '2a00:abcd:1234::100'
- '2a00:abcd:1234::101'
- '2a00:abcd:1234::200'
- '2a00:abcd:1234::201'
The tasks will do things like configure firewall and DB access, along the lines of:
- name: Allow web server access to DB server
  ufw:
    rule: allow
    name: mysql
    from_ip: "{{ item }}"
  loop: "{{ <loop expression goes here> }}"
It's what's in the loop expression that I'm having trouble with.
There are two parts to the query: extract the list of hosts, and then gather the ip addresses - doing it separately for ipv4 and ipv6 is fine.
I can get part way there with expressions like:
{{ server_ips | map('extract', groups['webservers']) }}
{{ server_ips | intersect(groups['webservers']) }}
However, both of these appear to flatten the result so though they find the right items, the ipv4 and ipv6 list elements are not there, so I can't proceed to the next step. Swapping the lists in these didn't help either. 
The subelements lookup seems a good way to get the IPs parts (though I actually need sub-subelements) and skip empty entries, but I can't see how to get to that point.
How should I do this lookup?
 
    