Search code examples
ansiblejinja2

How do I sort a list of strings by alphabetical order and length in Ansible?


I tried to sort Linux block device used by ansible_facts.

I want to sort by alphabetical order and length.

Expected result

sda, sdb ,sdc, ..., sdaa, sdab, ..., sdbb

Current result

sda, sdaa, sdab, sdac ..., sdb, sdb, sdba, sdbb, sdbc, ...

How can I sort sdaa device after sdz device?

- name: test
  hosts: localhost

  vars:
    - device: [sda,sdab,sdac,sdb,sdc,sdd,sde,sdf,sdg,sdh,sdi,sdj,sdk,sdl,sdm,sdn,sdo,sdp,sdq,sdr,sds,sdt,sdu,sdv,sdw,sdx,sdy,sdz,sdaa]

  tasks:
    - name: test
      debug:
        msg: "{{ device | sort }}"

The result is

ok: [localhost] => {
    "msg": [
        "sda",
        "sdaa",
        "sdab",
        "sdac",
        "sdb",
        "sdc",
        "sdd",
        "sde",
        "sdf",
        "sdg",
        "sdh",
        "sdi",
        "sdj",
        "sdk",
        "sdl",
        "sdm",
        "sdn",
        "sdo",
        "sdp",
        "sdq",
        "sdr",
        "sds",
        "sdt",
        "sdu",
        "sdv",
        "sdw",
        "sdx",
        "sdy",
        "sdz"
    ]
}

Solution

  • Flatten lists sorted by the length of the items. Create the below list of lists

        dmin: "{{ device|map('length')|min }}"
        dmax: "{{ device|map('length')|max }}"
    
        device_sorted_lists: |
          {% filter from_yaml %}
          {% for len in range(dmin|int, dmax|int + 1) %}
          {% set regex='^.{' ~ len ~ '}$' %}
          - {{ device|select('regex', regex)|sort }}
          {% endfor %}
          {% endfilter %}
    

    Flatten the lists

      device_sorted: "{{ device_sorted_lists|flatten }}"
    

    gives

      device_sorted:
      - sda
      - sdb
      - sdc
      - sdd
      - sde
      - sdf
      - sdg
      - sdh
      - sdi
      - sdj
      - sdk
      - sdl
      - sdm
      - sdn
      - sdo
      - sdp
      - sdq
      - sdr
      - sds
      - sdt
      - sdu
      - sdv
      - sdw
      - sdx
      - sdy
      - sdz
      - sdaa
      - sdab
      - sdac
    

    Example of a complete playbook for testing

    - hosts: all
    
      vars:
    
        device: [sda,sdab,sdac,sdb,sdc,sdd,sde,sdf,sdg,sdh,sdi,sdj,sdk,sdl,sdm,sdn,sdo,sdp,sdq,sdr,sds,sdt,sdu,sdv,sdw,sdx,sdy,sdz,sdaa]
    
        dmin: "{{ device|map('length')|min }}"
        dmax: "{{ device|map('length')|max }}"
    
        device_sorted_lists: |
          {% filter from_yaml %}
          {% for len in range(dmin|int, dmax|int + 1) %}
          {% set regex='^.{' ~ len ~ '}$' %}
          - {{ device|select('regex', regex)|sort }}
          {% endfor %}
          {% endfilter %}
    
        device_sorted: "{{ device_sorted_lists|flatten }}"
        
      tasks:
    
        - debug:
            var: device_sorted_lists
    
        - debug:
            var: device_sorted