Answers: <p><code>loop</code> requires a valid list. But we got here is,</p>
  
  <pre>
  [({'i_am': 'sam', 'eggs_ham': '456'}, <b>AnsibleUndefined</b>),
   ({'i_am': 'sam', 'eggs_ham': 'ham'}, {'transport': 'train'}),
   ({'i_am': 'eggs', 'eggs_ham': '456'}, <b>AnsibleUndefined</b>)]
  </pre>
  
  <p>Since no value is assigned to <code>transport_fact_results</code> when <code>common_thing_2</code> is not defined.</p>
  
  <p>if you remove the <code>when</code> condition it will work.</p>
  
  <p>CODE:</p>
  
  <pre><code>- hosts: localhost
    vars:
      default_thing_1: 123
      default_thing_2: 456
      default_transport: train
      list_of_things:
        - name: foo
          common_thing_1: "sam"
        - name: bar
          common_thing_1: "sam"
          common_thing_2: "ham"
        - name: biz
          common_thing_1: "eggs"
    tasks:
      - name: Set the Things Facts
        set_fact:
          thing:
            i_am: "{{ item.common_thing_1 | default(default_thing_1) }}"
            eggs_ham: "{{ item.common_thing_2 | default(default_thing_2) }}"
        loop: "{{ list_of_things }}"
        register: things_fact_results
      - name: Set Optional Things Facts
        set_fact:
          thing:
            transport: "{{ item.transport | default(default_transport) }}"
        when: "item.common_thing_2 is defined and item.common_thing_2 == 'ham'"
        loop: "{{ list_of_things }}"
        register: transport_fact_results
      - name: Debug the Facts
        debug:
          msg: "{{ item.0 | combine(item.1|default({})) }}"
        loop: "{{
          things_fact_results.results |
          map(attribute='ansible_facts.thing') | list |
          zip(
            transport_fact_results.results |
            map(attribute='ansible_facts.thing') | list
          ) | list
        }}"
  </code></pre>
  
  <p>OUTPUT:</p>
  
  <pre><code>PLAY [localhost] ************************************************************************************************************************************************************************************************
  
  TASK [Gathering Facts] ******************************************************************************************************************************************************************************************
  ok: [localhost]
  
  TASK [Set the Things Facts] *************************************************************************************************************************************************************************************
  ok: [localhost] =&gt; (item={u'common_thing_1': u'sam', u'name': u'foo'})
  ok: [localhost] =&gt; (item={u'common_thing_1': u'sam', u'common_thing_2': u'ham', u'name': u'bar'})
  ok: [localhost] =&gt; (item={u'common_thing_1': u'eggs', u'name': u'biz'})
  
  TASK [Set Optional Things Facts] ********************************************************************************************************************************************************************************
  ok: [localhost] =&gt; (item={u'common_thing_1': u'sam', u'name': u'foo'})
  ok: [localhost] =&gt; (item={u'common_thing_1': u'sam', u'common_thing_2': u'ham', u'name': u'bar'})
  ok: [localhost] =&gt; (item={u'common_thing_1': u'eggs', u'name': u'biz'})
  
  TASK [Debug the Facts] ******************************************************************************************************************************************************************************************
  ok: [localhost] =&gt; (item=[{u'eggs_ham': u'456', u'i_am': u'sam'}, {u'transport': u'train'}]) =&gt; {
      "msg": {
          "eggs_ham": "456", 
          "i_am": "sam", 
          "transport": "train"
      }
  }
  ok: [localhost] =&gt; (item=[{u'eggs_ham': u'ham', u'i_am': u'sam'}, {u'transport': u'train'}]) =&gt; {
      "msg": {
          "eggs_ham": "ham", 
          "i_am": "sam", 
          "transport": "train"
      }
  }
  ok: [localhost] =&gt; (item=[{u'eggs_ham': u'456', u'i_am': u'eggs'}, {u'transport': u'train'}]) =&gt; {
      "msg": {
          "eggs_ham": "456", 
          "i_am": "eggs", 
          "transport": "train"
      }
  }
  
  PLAY RECAP ******************************************************************************************************************************************************************************************************
  localhost                  : ok=4    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0  
  </code></pre>
  
Answers: CC BY-SA 4.0
Answers: 2020-04-30T22:05:09.220Z
Answers: The point behind the when condition is that the `transport` key being present in the object presents a problem for the task. An example of this would be some of the structure around the Kubernetes objects passed using the k8s module.
Answers: David Brown
Answers: CC BY-SA 4.0
Answers: 2020-04-16T07:17:49.387Z
Answers: 2020-04-16T07:17:49.387Z
Answers: Siva
Answers: <p>So the way I figured it out was to put the <code>when</code> condition into Jinja.</p>
  
  <pre><code>- hosts: localhost
    vars:
      default_thing_1: 123
      default_thing_2: 456
      default_transport: train
      list_of_things:
        - name: foo
          common_thing_1: "sam"
        - name: bar
          common_thing_1: "sam"
          common_thing_2: "ham"
        - name: biz
          common_thing_1: "eggs"
    tasks:
      - name: Set the Things Facts
        set_fact:
          thing:
            i_am: "{{ item.common_thing_1 | default(default_thing_1) }}"
            eggs_ham: "{{ item.common_thing_2 | default(default_thing_2) }}"
        loop: "{{ list_of_things }}"
        register: things_fact_results
      - name: Set Optional Things Facts
        set_fact:
          thing: |
            {% if item.common_thing_2 is defined and item.common_thing_2 == 'ham' %}
            { "transport": "{{ item.transport | default(default_transport) }}" }
            {% else %}
            {}
            {% endif %}
        loop: "{{ list_of_things }}"
        register: transport_fact_results
      - name: Debug the Facts
        debug:
          msg: "{{ item.0 | combine(item.1) }}"
        loop: "{{
          things_fact_results.results |
          map(attribute='ansible_facts.thing') | list |
          zip(
            transport_fact_results.results |
            map(attribute='ansible_facts.thing') | list
          ) | list
        }}"
  </code></pre>
  
  <p>This is kinda sloppy but does allow me to specify the else condition to set the result over every iteration to something instead of <code>AnsibleUndefined</code>.</p>
  
  <p>I don't really like this solution but it works.</p>
  
  <pre><code>$ ansible-playbook playbook.yml 
  [WARNING]: No inventory was parsed, only implicit localhost is available
  [WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'
  
  PLAY [localhost] *******************************************************************************************************************************************************************
  
  TASK [Gathering Facts] *************************************************************************************************************************************************************
  ok: [localhost]
  
  TASK [Set the Things Facts] ********************************************************************************************************************************************************
  ok: [localhost] =&gt; (item={'name': 'foo', 'common_thing_1': 'sam'})
  ok: [localhost] =&gt; (item={'name': 'bar', 'common_thing_1': 'sam', 'common_thing_2': 'ham'})
  ok: [localhost] =&gt; (item={'name': 'biz', 'common_thing_1': 'eggs'})
  
  TASK [Set Optional Things Facts] ***************************************************************************************************************************************************
  ok: [localhost] =&gt; (item={'name': 'foo', 'common_thing_1': 'sam'})
  ok: [localhost] =&gt; (item={'name': 'bar', 'common_thing_1': 'sam', 'common_thing_2': 'ham'})
  ok: [localhost] =&gt; (item={'name': 'biz', 'common_thing_1': 'eggs'})
  
  TASK [Debug the Facts] *************************************************************************************************************************************************************
  ok: [localhost] =&gt; (item=[{'i_am': 'sam', 'eggs_ham': '456'}, {}]) =&gt; {
      "msg": {
          "eggs_ham": "456",
          "i_am": "sam"
      }
  }
  ok: [localhost] =&gt; (item=[{'i_am': 'sam', 'eggs_ham': 'ham'}, {'transport': 'train'}]) =&gt; {
      "msg": {
          "eggs_ham": "ham",
          "i_am": "sam",
          "transport": "train"
      }
  }
  ok: [localhost] =&gt; (item=[{'i_am': 'eggs', 'eggs_ham': '456'}, {}]) =&gt; {
      "msg": {
          "eggs_ham": "456",
          "i_am": "eggs"
      }
  }
  
  PLAY RECAP *************************************************************************************************************************************************************************
  localhost                  : ok=4    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
  
  </code></pre>
  
  <p>Now the <code>transport</code> key is defined in the one object but not the other. So I can pass the item off to a complex object that's part of an API call.</p>
  
Answers: CC BY-SA 4.0
Answers: 2020-04-30T22:27:34.697Z
Answers: 2020-04-30T22:27:34.697Z
Answers: David Brown
Body: <p>I'm really trying to merge lists of objects along with defaults so I can loop over them and create resources in kubernetes. I've gotten it down to a simple(ish) playbook and will post it here.</p>
  
  <pre><code>- hosts: localhost
    vars:
      default_thing_1: 123
      default_thing_2: 456
      default_transport: train
      list_of_things:
        - name: foo
          common_thing_1: "sam"
        - name: bar
          common_thing_1: "sam"
          common_thing_2: "ham"
        - name: biz
          common_thing_1: "eggs"
    tasks:
      - name: Set the Things Facts
        set_fact:
          thing:
            i_am: "{{ item.common_thing_1 | default(default_thing_1) }}"
            eggs_ham: "{{ item.common_thing_2 | default(default_thing_2) }}"
        loop: "{{ list_of_things }}"
        register: things_fact_results
      - name: Set Optional Things Facts
        set_fact:
          thing:
            transport: "{{ item.transport | default(default_transport) }}"
        when: "item.common_thing_2 is defined and item.common_thing_2 == 'ham'"
        loop: "{{ list_of_things }}"
        register: transport_fact_results
      - name: Debug the Facts
        debug:
          msg: "{{ item.0 | combine(item.1|default({})) }}"
        loop: "{{
          things_fact_results.results |
          map(attribute='ansible_facts.thing') | list |
          zip(
            transport_fact_results.results |
            map(attribute='ansible_facts.thing') | list
          ) | list
        }}"
  </code></pre>
  
  <p>The output is as follows...</p>
  
  <pre><code>PLAY [localhost] ***********************************************************************************************************************************************************************************
  
  TASK [Gathering Facts] *****************************************************************************************************************************************************************************
  ok: [localhost]
  
  TASK [Set the Things Facts] ************************************************************************************************************************************************************************
  ok: [localhost] =&gt; (item={'name': 'foo', 'common_thing_1': 'sam'})
  ok: [localhost] =&gt; (item={'name': 'bar', 'common_thing_1': 'sam', 'common_thing_2': 'ham'})
  ok: [localhost] =&gt; (item={'name': 'biz', 'common_thing_1': 'eggs'})
  
  TASK [Set Optional Things Facts] *******************************************************************************************************************************************************************
  skipping: [localhost] =&gt; (item={'name': 'foo', 'common_thing_1': 'sam'}) 
  ok: [localhost] =&gt; (item={'name': 'bar', 'common_thing_1': 'sam', 'common_thing_2': 'ham'})
  skipping: [localhost] =&gt; (item={'name': 'biz', 'common_thing_1': 'eggs'}) 
  
  TASK [Debug the Facts] *****************************************************************************************************************************************************************************
  fatal: [localhost]: FAILED! =&gt; {"msg": "Invalid data passed to 'loop', it requires a list, got this instead: [({'i_am': 'sam', 'eggs_ham': '456'}, AnsibleUndefined), ({'i_am': 'sam', 'eggs_ham': 'ham'}, {'transport': 'train'}), ({'i_am': 'eggs', 'eggs_ham': '456'}, AnsibleUndefined)]. Hint: If you passed a list/dict of just one element, try adding wantlist=True to your lookup invocation or use q/query instead of lookup."}
  
  PLAY RECAP *****************************************************************************************************************************************************************************************
  localhost                  : ok=3    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0   
  
  </code></pre>
  
  <p>I think if I'm reading the loop item that's what I'm looking for. Though I'm not sure why ansible is refusing to perform the loop.</p>
  
  <p>Any help about how to merge lists of objects together with optional attributes that could or could not be set because k8s is pretty picky about what's being set and not when passing in objects.</p>
  
  <p>Thanks.</p>
  
Tags: <ansible>
Title: Combine Lists of Objects in Ansible
UserDisplayName: David Brown
ThreadText: # Combine Lists of Objects in Ansible
  
  (Asked by: username_0 on 2020-04-16)
  
  I'm really trying to merge lists of objects along with defaults so I can loop over them and create resources in kubernetes. I've gotten it down to a simple(ish) playbook and will post it here.
  
  ```
  - hosts: localhost
    vars:
      default_thing_1: 123
      default_thing_2: 456
      default_transport: train
      list_of_things:
        - name: foo
          common_thing_1: "sam"
        - name: bar
          common_thing_1: "sam"
          common_thing_2: "ham"
        - name: biz
          common_thing_1: "eggs"
    tasks:
      - name: Set the Things Facts
        set_fact:
          thing:
            i_am: "{{ item.common_thing_1 | default(default_thing_1) }}"
            eggs_ham: "{{ item.common_thing_2 | default(default_thing_2) }}"
        loop: "{{ list_of_things }}"
        register: things_fact_results
      - name: Set Optional Things Facts
        set_fact:
          thing:
            transport: "{{ item.transport | default(default_transport) }}"
        when: "item.common_thing_2 is defined and item.common_thing_2 == 'ham'"
        loop: "{{ list_of_things }}"
        register: transport_fact_results
      - name: Debug the Facts
        debug:
          msg: "{{ item.0 | combine(item.1|default({})) }}"
        loop: "{{
          things_fact_results.results |
          map(attribute='ansible_facts.thing') | list |
          zip(
            transport_fact_results.results |
            map(attribute='ansible_facts.thing') | list
          ) | list
        }}"
  
  ```
  
  The output is as follows...
  
  ```
  PLAY [localhost] ***********************************************************************************************************************************************************************************
  
  TASK [Gathering Facts] *****************************************************************************************************************************************************************************
  ok: [localhost]
  
  TASK [Set the Things Facts] ************************************************************************************************************************************************************************
  ok: [localhost] => (item={'name': 'foo', 'common_thing_1': 'sam'})
  ok: [localhost] => (item={'name': 'bar', 'common_thing_1': 'sam', 'common_thing_2': 'ham'})
  ok: [localhost] => (item={'name': 'biz', 'common_thing_1': 'eggs'})
  
  TASK [Set Optional Things Facts] *******************************************************************************************************************************************************************
  skipping: [localhost] => (item={'name': 'foo', 'common_thing_1': 'sam'}) 
  ok: [localhost] => (item={'name': 'bar', 'common_thing_1': 'sam', 'common_thing_2': 'ham'})
  skipping: [localhost] => (item={'name': 'biz', 'common_thing_1': 'eggs'}) 
  
  TASK [Debug the Facts] *****************************************************************************************************************************************************************************
  fatal: [localhost]: FAILED! => {"msg": "Invalid data passed to 'loop', it requires a list, got this instead: [({'i_am': 'sam', 'eggs_ham': '456'}, AnsibleUndefined), ({'i_am': 'sam', 'eggs_ham': 'ham'}, {'transport': 'train'}), ({'i_am': 'eggs', 'eggs_ham': '456'}, AnsibleUndefined)]. Hint: If you passed a list/dict of just one element, try adding wantlist=True to your lookup invocation or use q/query instead of lookup."}
  
  PLAY RECAP *****************************************************************************************************************************************************************************************
  localhost                  : ok=3    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0   
  
  ```
  
  I think if I'm reading the loop item that's what I'm looking for. Though I'm not sure why ansible is refusing to perform the loop.
  
  Any help about how to merge lists of objects together with optional attributes that could or could not be set because k8s is pretty picky about what's being set and not when passing in objects.
  
  Thanks.
  
  **Tags:** ansible
  
  
  ---
  
  ## 2 answers
  
  ### Answer by username_1 on 2020-04-16. Score: 1
  
  `loop` requires a valid list. But we got here is,
  
  ```
  
  [({'i_am': 'sam', 'eggs_ham': '456'}, AnsibleUndefined),
   ({'i_am': 'sam', 'eggs_ham': 'ham'}, {'transport': 'train'}),
   ({'i_am': 'eggs', 'eggs_ham': '456'}, AnsibleUndefined)]
  
  ```
  
  Since no value is assigned to `transport_fact_results` when `common_thing_2` is not defined.
  
  if you remove the `when` condition it will work.
  
  CODE:
  
  ```
  - hosts: localhost
    vars:
      default_thing_1: 123
      default_thing_2: 456
      default_transport: train
      list_of_things:
        - name: foo
          common_thing_1: "sam"
        - name: bar
          common_thing_1: "sam"
          common_thing_2: "ham"
        - name: biz
          common_thing_1: "eggs"
    tasks:
      - name: Set the Things Facts
        set_fact:
          thing:
            i_am: "{{ item.common_thing_1 | default(default_thing_1) }}"
            eggs_ham: "{{ item.common_thing_2 | default(default_thing_2) }}"
        loop: "{{ list_of_things }}"
        register: things_fact_results
      - name: Set Optional Things Facts
        set_fact:
          thing:
            transport: "{{ item.transport | default(default_transport) }}"
        when: "item.common_thing_2 is defined and item.common_thing_2 == 'ham'"
        loop: "{{ list_of_things }}"
        register: transport_fact_results
      - name: Debug the Facts
        debug:
          msg: "{{ item.0 | combine(item.1|default({})) }}"
        loop: "{{
          things_fact_results.results |
          map(attribute='ansible_facts.thing') | list |
          zip(
            transport_fact_results.results |
            map(attribute='ansible_facts.thing') | list
          ) | list
        }}"
  
  ```
  
  OUTPUT:
  
  ```
  PLAY [localhost] ************************************************************************************************************************************************************************************************
  
  TASK [Gathering Facts] ******************************************************************************************************************************************************************************************
  ok: [localhost]
  
  TASK [Set the Things Facts] *************************************************************************************************************************************************************************************
  ok: [localhost] => (item={u'common_thing_1': u'sam', u'name': u'foo'})
  ok: [localhost] => (item={u'common_thing_1': u'sam', u'common_thing_2': u'ham', u'name': u'bar'})
  ok: [localhost] => (item={u'common_thing_1': u'eggs', u'name': u'biz'})
  
  TASK [Set Optional Things Facts] ********************************************************************************************************************************************************************************
  ok: [localhost] => (item={u'common_thing_1': u'sam', u'name': u'foo'})
  ok: [localhost] => (item={u'common_thing_1': u'sam', u'common_thing_2': u'ham', u'name': u'bar'})
  ok: [localhost] => (item={u'common_thing_1': u'eggs', u'name': u'biz'})
  
  TASK [Debug the Facts] ******************************************************************************************************************************************************************************************
  ok: [localhost] => (item=[{u'eggs_ham': u'456', u'i_am': u'sam'}, {u'transport': u'train'}]) => {
      "msg": {
          "eggs_ham": "456", 
          "i_am": "sam", 
          "transport": "train"
      }
  }
  ok: [localhost] => (item=[{u'eggs_ham': u'ham', u'i_am': u'sam'}, {u'transport': u'train'}]) => {
      "msg": {
          "eggs_ham": "ham", 
          "i_am": "sam", 
          "transport": "train"
      }
  }
  ok: [localhost] => (item=[{u'eggs_ham': u'456', u'i_am': u'eggs'}, {u'transport': u'train'}]) => {
      "msg": {
          "eggs_ham": "456", 
          "i_am": "eggs", 
          "transport": "train"
      }
  }
  
  PLAY RECAP ******************************************************************************************************************************************************************************************************
  localhost                  : ok=4    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0  
  
  ```
  
  > **Comments:**
  > **username_0**: The point behind the when condition is that the `transport` key being present in the object presents a problem for the task. An example of this would be some of the structure around the Kubernetes objects passed using the k8s module.  
  
  ---
  
  ### Answer by username_0 on 2020-04-30. Score: 0
  
  So the way I figured it out was to put the `when` condition into Jinja.
  
  ```
  - hosts: localhost
    vars:
      default_thing_1: 123
      default_thing_2: 456
      default_transport: train
      list_of_things:
        - name: foo
          common_thing_1: "sam"
        - name: bar
          common_thing_1: "sam"
          common_thing_2: "ham"
        - name: biz
          common_thing_1: "eggs"
    tasks:
      - name: Set the Things Facts
        set_fact:
          thing:
            i_am: "{{ item.common_thing_1 | default(default_thing_1) }}"
            eggs_ham: "{{ item.common_thing_2 | default(default_thing_2) }}"
        loop: "{{ list_of_things }}"
        register: things_fact_results
      - name: Set Optional Things Facts
        set_fact:
          thing: |
            {% if item.common_thing_2 is defined and item.common_thing_2 == 'ham' %}
            { "transport": "{{ item.transport | default(default_transport) }}" }
            {% else %}
            {}
            {% endif %}
        loop: "{{ list_of_things }}"
        register: transport_fact_results
      - name: Debug the Facts
        debug:
          msg: "{{ item.0 | combine(item.1) }}"
        loop: "{{
          things_fact_results.results |
          map(attribute='ansible_facts.thing') | list |
          zip(
            transport_fact_results.results |
            map(attribute='ansible_facts.thing') | list
          ) | list
        }}"
  
  ```
  
  This is kinda sloppy but does allow me to specify the else condition to set the result over every iteration to something instead of `AnsibleUndefined`.
  
  I don't really like this solution but it works.
  
  ```
  $ ansible-playbook playbook.yml 
  [WARNING]: No inventory was parsed, only implicit localhost is available
  [WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'
  
  PLAY [localhost] *******************************************************************************************************************************************************************
  
  TASK [Gathering Facts] *************************************************************************************************************************************************************
  ok: [localhost]
  
  TASK [Set the Things Facts] ********************************************************************************************************************************************************
  ok: [localhost] => (item={'name': 'foo', 'common_thing_1': 'sam'})
  ok: [localhost] => (item={'name': 'bar', 'common_thing_1': 'sam', 'common_thing_2': 'ham'})
  ok: [localhost] => (item={'name': 'biz', 'common_thing_1': 'eggs'})
  
  TASK [Set Optional Things Facts] ***************************************************************************************************************************************************
  ok: [localhost] => (item={'name': 'foo', 'common_thing_1': 'sam'})
  ok: [localhost] => (item={'name': 'bar', 'common_thing_1': 'sam', 'common_thing_2': 'ham'})
  ok: [localhost] => (item={'name': 'biz', 'common_thing_1': 'eggs'})
  
  TASK [Debug the Facts] *************************************************************************************************************************************************************
  ok: [localhost] => (item=[{'i_am': 'sam', 'eggs_ham': '456'}, {}]) => {
      "msg": {
          "eggs_ham": "456",
          "i_am": "sam"
      }
  }
  ok: [localhost] => (item=[{'i_am': 'sam', 'eggs_ham': 'ham'}, {'transport': 'train'}]) => {
      "msg": {
          "eggs_ham": "ham",
          "i_am": "sam",
          "transport": "train"
      }
  }
  ok: [localhost] => (item=[{'i_am': 'eggs', 'eggs_ham': '456'}, {}]) => {
      "msg": {
          "eggs_ham": "456",
          "i_am": "eggs"
      }
  }
  
  PLAY RECAP *************************************************************************************************************************************************************************
  localhost                  : ok=4    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
  
  ```
  
  Now the `transport` key is defined in the one object but not the other. So I can pass the item off to a complex object that's part of an API call.
  
  
  ---
  
  