简体   繁体   中英

Python order Dict with a pre-defined order

Receive a dict and return a new ordered dict

A json returned from an api:

{
    'host': '192.168.0.1',
    'name': 'my_new_name',
    'port': 443,
    'something_more': 'hello'
}

The goal:

{
    'name': 'my_new_name',
    'something_more': 'hello',
    'host': '192.168.0.1',
    'port': 443
}

there is some way where I can define the order of keys?

For example to achieve this order on goal :

key_order = ('name', 'something_more', 'host', 'port')

Thanks

You can use a list comprehension to feed a list of tuples to collections.OrderedDict :

from collections import OrderedDict

d = {'host': '192.168.0.1', 'name': 'my_new_name',
     'port': 443, 'something_more': 'hello'}

key_order = ('name', 'something_more', 'host', 'port')
res = OrderedDict([(k, d[k]) for k in key_order])

OrderedDict([('name', 'my_new_name'),
             ('something_more', 'hello'),
             ('host', '192.168.0.1'),
             ('port', 443)])

Works for Python 2.x onwards. For Python 3.7+, you can rely on insertion ordering and use a regular dictionary.

If you're using Python 3.7 or later*, you can specify the order of items in your dict, because insertion order is preserved. Create a new dictionary and insert the items in whatever order you like:

def reorder_items(d, keys):
    d = d.copy() #we're going to destructively modify d, so make a copy first
    result = {}
    for key in keys:
        if key in d:
            result[key] = d.pop(key)
    #the user might not have supplied all the keys belonging to d, 
    #so insert anything we haven't touched yet
    result.update(d)
    return result

d = {
    'host': '192.168.0.1',
    'name': 'my_new_name',
    'port': 443,
    'something_more': 'hello'
}

desired_key_order = ('name', 'something_more', 'host', 'port')
goal = reorder_items(d, desired_key_order)
print(goal)

Result:

{'name': 'my_new_name', 'something_more': 'hello', 'host': '192.168.0.1', 'port': 443}

(*you can also do this in CPython 3.6, but this is an implementation detail which should not be relied upon)

Prior to 3.7, you can't directly control how items in a dict are ordered. But you can use the collections.OrderedDict type. Use the function in the previous code block, switching out result = {} with result = collections.OrderedDict() .


A more concise (albeit slightly opaque) approach is:

result = {key:d[key] for category in (desired_key_order, d.keys()) for key in category if key in d}
#or, for versions prior to 3.7,
result = collections.OrderedDict((key, d[key]) for category in (desired_key_order, d.keys()) for key in category if key in d)

This takes advantage of the fact that dict comprehensions and OrderedDict can be constructed with duplicate keys, while staying in order in relation to the first appearance of each key.

This is still a little longer than jpp's solution, since it is trying to be a bit more error-tolerant. It works even if d contains keys that desired_key_order doesn't, and vice-versa. Items whose order is not specified will appear in the result after items that do have a specified order.

Ordered Dictionary is the thing you need my friend.]

from collections import OrderedDict
d = OrderedDict([
('name', 'my_new_name'),
('something_more', 'hello'),
('host', '192.168.0.1'),
('port', 443)
])

Edit: this only works in Python 3.6+. Also feed the OrderedDict with a list of tuples instead of a dictionary to maintain the order of key-value pairs.

If you're on Python <3.7, OrderedDict preserves the order of the items:

from collections import OrderedDict

d = OrderedDict([
        ('name', 'my_new_name'),
        ('something_more', 'hello'),
        ('host', '192.168.0.1'),
        ('port', 443)
    ])

# OrderedDict([('name', 'my_new_name'), ('something_more', 'hello'), ('host', '192.168.0.1'), ('port', 443)])

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM