Search code examples
shellcommand-lineyamlsliceyq

yq: Sorting a slice of an array in a YAML file


Sample file.yml:

---
- foo: bar
  bing:
    - bong
    - bang
  people:
    - { name: pin, nicknames: ['pin', 'first'] }
    - { name: deb, nicknames: ['deb'] }
    - { name: roger, nicknames: ['roger'] }
    - { name: kenny, nicknames: ['kenny'] }
    - { name: annie, nicknames: ['annie'] }

I'm trying to use yq to sort people objects by name, except for pin which should stay first.

Desired output:

---
- foo: bar
  bing:
    - bong
    - bang
  people:
    - { name: pin, nicknames: ['pin', 'first'] }
    - { name: annie, nicknames: ['annie'] }
    - { name: deb, nicknames: ['deb'] }
    - { name: kenny, nicknames: ['kenny'] }
    - { name: roger, nicknames: ['roger'] }
❯ yq -V
yq (https://github.com/mikefarah/yq/) version v4.31.2

I got as far as slicing and sorting but that only prints the sorted slice:

❯ yq '.[].people.[1:] | sort_by(.name)' file.yml
- {name: annie, nicknames: ['annie']}
- {name: deb, nicknames: ['deb']}
- {name: kenny, nicknames: ['kenny']}
- {name: roger, nicknames: ['roger']}

Not sure how to get the rest back in, or if I'm even on the right track.


Solution

  • ❯ yq '.[].people.[1:] | sort_by(.name)' file.yml

    Actually, changing only | to |= in order to update the array slice while retaining the original context, should do the job (and it indeed does with kislyuk/yq). With mikefarah/yq, however, the array for some reason is returned unsorted:

    yq '.[].people.[1:] |= sort_by(.name)' file.yml
    
    ---
    - foo: bar
      bing:
        - bong
        - bang
      people:
        - {name: pin, nicknames: ['pin', 'first']}
        - {name: deb, nicknames: ['deb']}
        - {name: roger, nicknames: ['roger']}
        - {name: kenny, nicknames: ['kenny']}
        - {name: annie, nicknames: ['annie']}
    

    So, instead, you could do it manually by slicing off the first item, then re-adding it to the independently sorted array slice:

    yq '.[].people |= .[:1] + (.[1:] | sort_by(.name))' file.yml
    
    ---
    - foo: bar
      bing:
        - bong
        - bang
      people:
        - {name: pin, nicknames: ['pin', 'first']}
        - {name: annie, nicknames: ['annie']}
        - {name: deb, nicknames: ['deb']}
        - {name: kenny, nicknames: ['kenny']}
        - {name: roger, nicknames: ['roger']}