Search code examples
dictionarypython-3.6dictionary-comprehension

cpython 3.6 dict order in dict comprehension


Since in CPython 3.6 dicts are ordered (I know it's not guaranteed - yet awesome to use), so I expected the following dict comprehension to preserve the order:

# attempt to get only specific k:v from dict jrn_blocks in order
jrn_blocks = {"header":0, "open":1, "detached":2, "rps_command":3, "close_no_save":4}
recip = "header,open,close_no_save"
{k: v for k, v in jrn_blocks.items() if k in recip}
# -> gives me: {'close_no_save': 4, 'header': 0, 'open': 1}
# -> I would expect: {'header': 0, 'open': 1, 'close_no_save': 4}

If I just use a list comprehension on item tuples, order is preserved:

[(k, v) for k, v in jrn_blocks.items() if k in recip]
# -> gives me: [('header', 0), ('open', 1), ('close_no_save', 4)]

As soon as I try to create a dictionary from these tuples, the order is lost again:

dict([(k, v) for k, v in jrn_blocks.items() if k in recip])
# -> gives me: {'close_no_save': 4, 'header': 0, 'open': 1}

Is this expected behavior?
Is there an alphabetical key sorting happening in the background?
Or is there another elegant way to do this kind of "dict filter" while preserving order?
Thank you so much in advance!


Solution

  • I finally found out myself:
    I was fooled by what I got returned from the REPL when I typed:

    {k: v for k, v in jrn_blocks.items() if k in recip}
    # {'close_no_save': 4, 'header': 0, 'open': 1}
    

    but when I stored the result of the dictionary comprehension in a variable, I found the keys were in the expected order:

    jd = {k: v for k, v in jrn_blocks.items() if k in recip}
    print(jd.keys())
    # {'header': 0, 'open': 1, 'close_no_save': 4}