sadok-f
sadok-f

Reputation: 1457

Ansible list not ordered

I have a list I'm looping over, the issue is it gives a weird order.

param.yml file:

my_list:
  a: val1
  b: val2
  c: val3
  d: val3

Here my simple debug loop:

- name: debug
  debug:
    msg: "{{ item }}"
  loop: "{{ my_list | list }}"

Here the output:

ok: [host] => (item=a) => {
    "msg": "a"
}
ok: [host] => (item=c) => {
    "msg": "c"
}
ok: [host] => (item=b) => {
    "msg": "b"
}
ok: [host] => (item=d) => {
    "msg": "d"
}

I'm using ansible 2.10 with python 2.7.5:

ansible --version
ansible 2.10.2
  config file = /home/ansible.cfg
  configured module search path = [u'/home/ansible/library']
  ansible python module location = /usr/lib/python2.7/site-packages/ansible
  executable location = /bin/ansible
  python version = 2.7.5 (default, Apr  2 2020, 13:16:51) [GCC 4.8.5 20150623 (Red Hat 4.8.5-39)]

The problem when I switch to python3, it is working as expected:

ok: [host] => (item=a) => {
    "msg": "a"
}
ok: [host] => (item=b) => {
    "msg": "b"
}
ok: [host] => (item=c) => {
    "msg": "c"
}
ok: [host] => (item=d) => {
    "msg": "d"
}

Unfortunately, I cannot upgrade the environment to python3 and I need the list to be in the same order as in the param.yml file.

Thank you!

Upvotes: 3

Views: 314

Answers (1)

Vladimir Botka
Vladimir Botka

Reputation: 68064

Variable my_list is not a list. It's a dictionary. There is no guarantee in which order a dictionary's keys will be iterated. Use sort filter. For example,

  my_keys: "{{ my_list|list|sort }}"

gives

  my_keys: [a, b, c, d]

Q: "I need to iterate over it in the same order as it is in the param.yml"

A: It's the wrong data structure for a use case. YAML mapping is an unordered set. You can't "iterate over it in the same order" because there is no order. Use a list of keys. For example,

    - debug:
        msg: "{{ item }}: {{ my_list[item] }}"
      loop: "{{ my_keys }}"

gives (abridged)

  msg: 'a: val1'
  msg: 'b: val2'
  msg: 'c: val3'
  msg: 'd: val4'
  • The results are strings. Create JSON if you want to get dictionaries
    - debug:
        msg: '{ "{{ item }}": "{{ my_list[item] }}" }'
      loop: "{{ my_keys }}"

gives (abridged)

  msg:
    a: val1
  msg:
    b: val2
  msg:
    c: val3
  msg:
    d: val4
  • The next option is using the filter dict2items and converting the dictionary to a list. Use the filters sort and sort the list by the attribute key
    - debug:
        msg: "{{ item }}"
      loop: "{{ my_list|dict2items|sort(attribute='key') }}"

gives (abridged)

  msg:
    key: a
    value: val1
  msg:
    key: b
    value: val2
  msg:
    key: c
    value: val3
  msg:
    key: d
    value: val4
  • But, if you want to keep the order, the simplest option is to put the data into a list
    - debug:
        msg: "{{ item }}"
      loop: "{{ my_list }}"
      vars:
        my_list:
          - a: val1
          - b: val2
          - c: val3
          - d: val4

Example of a complete playbook for testing

- hosts: all

  vars:

    my_list:
      a: val1
      b: val2
      c: val3
      d: val4

    my_keys: "{{ my_list|list|sort }}"
    my_key2: "{{ my_list.keys()|sort }}"
    
  tasks:

    - debug:
        var: my_keys|to_yaml
    - debug:
        var: my_key2|to_yaml

    - debug:
        msg: "{{ item }}: {{ my_list[item] }}"
      loop: "{{ my_keys }}"
      tags: t1

    - debug:
        msg: '{ "{{ item }}": "{{ my_list[item] }}" }'
      loop: "{{ my_keys }}"
      tags: t2

    - debug:
        msg: "{{ item }}"
      loop: "{{ my_list|dict2items|sort(attribute='key') }}"
      tags: t3

    - debug:
        msg: "{{ item }}"
      loop: "{{ my_list }}"
      vars:
        my_list:
          - a: val1
          - b: val2
          - c: val3
          - d: val4
      tags: t4

Upvotes: 4

Related Questions