Search code examples
pythondictionaryyamlpyyaml

Can PyYAML dump dict items in non-alphabetical order?


I'm using yaml.dump to output a dict. It prints out each item in alphabetical order based on the key.

>>> d = {"z":0,"y":0,"x":0}
>>> yaml.dump( d, default_flow_style=False )
'x: 0\ny: 0\nz: 0\n'

Is there a way to control the order of the key/value pairs?

In my particular use case, printing in reverse would (coincidentally) be good enough. For completeness though, I'm looking for an answer that shows how to control the order more precisely.

I've looked at using collections.OrderedDict but PyYAML doesn't (seem to) support it. I've also looked at subclassing yaml.Dumper, but I haven't been able to figure out if it has the ability to change item order.


Solution

  • There's probably a better workaround, but I couldn't find anything in the documentation or the source.


    Python 2 (see comments)

    I subclassed OrderedDict and made it return a list of unsortable items:

    from collections import OrderedDict
    
    class UnsortableList(list):
        def sort(self, *args, **kwargs):
            pass
    
    class UnsortableOrderedDict(OrderedDict):
        def items(self, *args, **kwargs):
            return UnsortableList(OrderedDict.items(self, *args, **kwargs))
    
    yaml.add_representer(UnsortableOrderedDict, yaml.representer.SafeRepresenter.represent_dict)
    

    And it seems to work:

    >>> d = UnsortableOrderedDict([
    ...     ('z', 0),
    ...     ('y', 0),
    ...     ('x', 0)
    ... ])
    >>> yaml.dump(d, default_flow_style=False)
    'z: 0\ny: 0\nx: 0\n'
    

    Python 3 or 2 (see comments)

    You can also write a custom representer, but I don't know if you'll run into problems later on, as I stripped out some style checking code from it:

    import yaml
    
    from collections import OrderedDict
    
    def represent_ordereddict(dumper, data):
        value = []
    
        for item_key, item_value in data.items():
            node_key = dumper.represent_data(item_key)
            node_value = dumper.represent_data(item_value)
    
            value.append((node_key, node_value))
    
        return yaml.nodes.MappingNode(u'tag:yaml.org,2002:map', value)
    
    yaml.add_representer(OrderedDict, represent_ordereddict)
    

    But with that, you can use the native OrderedDict class.