Creating OrderedDict from list does not retain order [duplicate] - python

I am having some trouble using the collections.OrderedDict class. I am using Python 2.7 on Raspbian, the Debian distro for Raspberry Pi. I am trying to print two dictionaries in order for comparison (side-by-side) for a text-adventure. The order is essential to compare accurately.
No matter what I try the dictionaries print in their usual unordered way.
Here's what I get when I do it on my RPi:
import collections
ship = {"NAME": "Albatross",
"HP":50,
"BLASTERS":13,
"THRUSTERS":18,
"PRICE":250}
ship = collections.OrderedDict(ship)
print ship
# OrderedDict([('PRICE', 250), ('HP', 50), ('NAME', 'Albatross'), ('BLASTERS', 13), ('THRUSTERS', 18)])
Obviously there is something not right because it is printing the function call and putting the keys and value groups into a nested list...
This is what I got by running something similar on my PC:
import collections
Joe = {"Age": 28, "Race": "Latino", "Job": "Nurse"}
Bob = {"Age": 25, "Race": "White", "Job": "Mechanic", "Random": "stuff"}
#Just for clarity:
Joe = collections.OrderedDict(Joe)
Bob = collections.OrderedDict(Bob)
print Joe
# OrderedDict([('Age', 28), ('Race', 'Latino'), ('Job', 'Nurse')])
print Bob
# OrderedDict([('Age', 25), ('Race', 'White'), ('Job', 'Mechanic'), ('Random', 'stuff')])
This time, it is in order, but it shouldn't be printing the other things though right? (The putting it into list and showing function call.)
Where am I making my error? It shouldn't be anything to do with the pi version of Python because it is just the Linux version.

You are creating a dictionary first, then passing that dictionary to an OrderedDict. For Python versions < 3.6 (*), by the time you do that, the ordering is no longer going to be correct. dict is inherently not ordered.
Pass in a sequence of tuples instead:
ship = [("NAME", "Albatross"),
("HP", 50),
("BLASTERS", 13),
("THRUSTERS", 18),
("PRICE", 250)]
ship = collections.OrderedDict(ship)
What you see when you print the OrderedDict is it's representation, and it is entirely correct. OrderedDict([('PRICE', 250), ('HP', 50), ('NAME', 'Albatross'), ('BLASTERS', 13), ('THRUSTERS', 18)]) just shows you, in a reproducable representation, what the contents are of the OrderedDict.
(*): In the CPython 3.6 implementation, the dict type was updated to use a more memory efficient internal structure that has the happy side effect of preserving insertion order, and by extension the code shown in the question works without issues. As of Python 3.7, the Python language specification has been updated to require that all Python implementations must follow this behaviour. See this other answer of mine for details and also why you'd still may want to use an OrderedDict() for certain cases.

If you can't edit this part of code where your dict was defined you can still order it at any point in any way you want, like this:
from collections import OrderedDict
order_of_keys = ["key1", "key2", "key3", "key4", "key5"]
list_of_tuples = [(key, your_dict[key]) for key in order_of_keys]
your_dict = OrderedDict(list_of_tuples)

Most of the time we go for OrderedDict when we required a custom order not a generic one like ASC etc.
Here is the proposed solution:
import collections
ship = {"NAME": "Albatross",
"HP":50,
"BLASTERS":13,
"THRUSTERS":18,
"PRICE":250}
ship = collections.OrderedDict(ship)
print ship
new_dict = collections.OrderedDict()
new_dict["NAME"]=ship["NAME"]
new_dict["HP"]=ship["HP"]
new_dict["BLASTERS"]=ship["BLASTERS"]
new_dict["THRUSTERS"]=ship["THRUSTERS"]
new_dict["PRICE"]=ship["PRICE"]
print new_dict
This will be output:
OrderedDict([('PRICE', 250), ('HP', 50), ('NAME', 'Albatross'), ('BLASTERS', 13), ('THRUSTERS', 18)])
OrderedDict([('NAME', 'Albatross'), ('HP', 50), ('BLASTERS', 13), ('THRUSTERS', 18), ('PRICE', 250)])
Note: The new sorted dictionaries maintain their sort order when entries are deleted. But when new keys are added, the keys are appended to the end and the sort is not maintained.(official doc)

You can create the ordered dict from old dict in one line:
from collections import OrderedDict
ordered_dict = OrderedDict(sorted(ship.items())
The default sorting key is by dictionary key, so the new ordered_dict is sorted by old dict's keys.

Use dict.items(); it can be as simple as following:
ship = collections.OrderedDict(ship.items())

Related

How to prevent Python dictionary key ordering reversal?

I have the following python code snippet involving dictionary,
sample_dict = {
"name": "Kelly",
"age": 25,
"salary": 8000,
"city": "New york"
}
keys = ["name", "salary"]
sample_dict = {k: sample_dict[k] for k in sample_dict.keys() - keys}
print(sample_dict)
Why the output is {'city': 'New york', 'age': 25} and not {'age': 25, 'city': 'New york'}?
What causes the reverse ordering of dictionary keys? Any help will be highly appreciated. I executed the code on Spyder and Python 3.8.5 version.
I guess the order is lost during processing of - between your keys() and your keys list (because it involves conversion to sets which are unordered). You can build your comprehension as follow to prevent it.
keys = {"name", "salary"}
sample_dict = {k: sample_dict[k] for k in sample_dict.keys() if k not in keys}
#Tranbi has provided the solution to the OP. I just supplement some materials.
Although dict preserves insertion order since 3.7.
Changed in version 3.7: Dictionary order is guaranteed to be insertion order. This behavior was an implementation detail of CPython from 3.6.
set does not.
Being an unordered collection, sets do not record element position or order of insertion.
If you check the result of sample_dict.keys() - keys, you will find it is a set.
remain_keys = sample_dict.keys() - keys
print(type(remain_keys))
# set
for k in remain_keys:
print(k)
# city
# age
Because dict.keys() returns a set-like view.
Keys views are set-like since their entries are unique and hashable. ... For set-like views, all of the operations defined for the abstract base class collections.abc.Set are available (for example, ==, <, or ^).

How to change the order of keys in a Python 3.5 dictionary, using another list as a reference for keys?

I'm trying to change the order of 'keys' in a dictionary, with no success.
This is my initial dictionary:
Not_Ordered={
'item':'book',
'pages':200,
'weight':1.0,
'price':25,
'city':'London'
}
Is there a chance for me to change the order according to a key-order list, like this:
key_order=['city', 'pages', 'item', 'weight', 'price']
note:
I'm using Python 3.5.2.
I'm not looking for sorting the keys.
I'm aware that Python 3.6 follows the insertion.
Also, I'm aware of OrderedDict, but it gives me a list, but I'm
looking for a dictionary as final result.
Dicts are "officially" maintained in insertion order starting in 3.7. They were so ordered in 3.6, but it wasn't guaranteed before 3.7. Before 3.6, there is nothing you can do to affect the order in which keys appear.
But OrderedDict can be used instead. I don't understand your "but it gives me a list" objection - I can't see any sense in which that's actually true.
Your example:
>>> from collections import OrderedDict
>>> d = OrderedDict([('item', 'book'), ('pages', 200),
... ('weight', 1.0), ('price', 25),
... ('city', 'London')])
>>> d # keeps the insertion order
OrderedDict([('item', 'book'), ('pages', 200), ('weight', 1.0), ('price', 25), ('city', 'London')])
>>> key_order= ['city', 'pages', 'item', 'weight', 'price'] # the order you want
>>> for k in key_order: # a loop to force the order you want
... d.move_to_end(k)
>>> d # which works fine
OrderedDict([('city', 'London'), ('pages', 200), ('item', 'book'), ('weight', 1.0), ('price', 25)])
Don't be confused by the output format! d is displayed as a list of pairs, passed to an OrderedDict constructor, for clarity. d isn't itself a list.
no solution in Python 3.5
for python >= 3.6, just
Ordered_Dict = {k : Not_Ordered_Dict[k] for k in key_order}
Your problem here is that dictionaries in Python don't have an insertion order so you can't "order" them. As you can see here: Official Python Docs on Dictionaries.
If you provide more insight on whats the purpose of this we might try to solve it in other way.
you still can use sorted and specify the items().
ordered_dict_items = sorted(Not_Ordered.items(), key=lambda x: key_order.index(x[0]))
return dict((x, y) for x, y in ordered_dict_items) # to convert tuples to dict
or directly using lists:
ordered_dict_items = [(k, Not_Ordered[k]) for k in key_order]
return dict((x, y) for x, y in ordered_dict_items) # to convert tuples to

Python - How to access first type of data

NumberTextSet3 = {"ten": 10,
"hundred": 100,
"thousand": 1000,
"million": 1000000,
"billion": 1000000000,
"trillion": 1000000000000}
In this dictionary, I can access the number 1000000000000 by using NumberTextSet3["trillion"] .
But how would I access the the last word in the dictionary, maybe like: NumberTextSet3[-1] and have it return "trillion"?
There is no last word in a dictionary.
To check it, try,
print NumberTextSet3
You will get different ordered result in different time.
You can slightly modify your data structure to [("ten",10),("hundred",100),...]
Now, you can use it with index.
For example,
a=[("ten",10),("hundred",100)]
print a[0][0]
print a[1][1]
Output:
ten
100
You can use an OrderedDict too
I would use a OrderedDict like this:
from collections import OrderedDict
NumberTextSet3 = OrderedDict([("ten", 10),
("hundred", 100),
("thousand", 1000),
("million", 1000000),
("billion", 1000000000),
("trillion", 1000000000000)])
# On Python2 this will work:
print NumberTextSet3.keys()[-1]
# This is a little bit longer but will work in Python2 and Python3:
print list(NumberTextSet3.keys())[-1]
A dictionary is not ordered so it has no such thing as "first" or "last" entry. You can access a specific key as follows:
NumberTextSet3["trillion"]
or:
NumberTextSet3.get("trillion")
If you want to access the list of keys you can do:
NumberTextSet3.keys()
which return on my computer: ['billion', 'trillion', 'ten', 'thousand', 'million', 'hundred']
and as you can see - there is no "order" here.
You would need to extract all of the entries and then sort them:
import operator
NumberTextSet3 = {"ten": 10,
"hundred": 100,
"thousand": 1000,
"million": 1000000,
"billion": 1000000000,
"trillion": 1000000000000}
values = sorted(NumberTextSet3.items(), key=operator.itemgetter(1))
print values[-1][0]
Giving:
trillion
If you wanted to do the reverse lookup, then you could create the inverse mapping as follows, and use that:
InverseNumberTextSet3 = {v: k for k, v in NumberTextSet3.items()}
print InverseNumberTextSet3[1000000000000]
Giving:
trillion
This can work although it's not guaranteed since dictionaries are not ordered.
NumberTextSet3.items()[-1][0]
Edit: You may want to use a list of tuples (below) instead; in which case the same technique would work since items() gives the dict in key/value pairs [tuple].
NumberTextSet3 = (("ten", 10),
("hundred", 100),
("thousand", 1000),
("million", 1000000),
("billion", 1000000000),
("trillion", 1000000000000))
Python dictionaries have no ordering guarantee.
Implementation-wise this stems from the fact that the dictionary is a hash map and the hashmap has its key ordering determined by the hash function (which is supposed to be pseudo-random).
So, while you can access the first and last items in your dictionary (NumberTextSet3.items()[0] and NumberTextSet3.items()[-1], respectively) these probably do not correspond the order in which you created the dictionary.
However, the standard library provides collections.OrderedDict, which preserves insertion order, hence
from collections import OrderedDict
NumberTextSet3 = OrderedDict((
("ten", 10),
("hundred", 100),
("thousand", 1000),
("million", 1000000),
("billion", 1000000000),
("trillion", 1000000000000)
))
print(list(NumberTextSet3.keys()))
=> ['ten', 'hundred', 'thousand', 'million', 'billion', 'trillion']
print(list(dict(NumberTextSet3).keys()))
=> ['million', 'hundred', 'ten', 'billion', 'thousand', 'trillion']
Be aware of the fact that I change the dictionary literal to a tuple list. Using a dictionary literal would not work, since this would create a temporary, unordered dictionary and than pass that to OrderedDict, which would freeze the (quasi-random) order.

Add object to start of dictionary

I am making a group chatting app and I have images associated with the users, so whenever they say something, their image is displayed next to it. I wrote the server in python and the client will be an iOS app. I use a dictionary to store all of the message/image pairs. Whenever my iOS app sends a command to the server (msg:<message), the dictionary adds the image and message to the dictionary like so:dictionary[message] = imageName, which is converted to lists then strings to be sent off in a socket. I would like to add the incoming messages to the start of the dictionary, instead of the end. Something like
#When added to end:
dictionary = {"hello":image3.png}
#new message
dictionary = {"hello":image3.png, "i like py":image1.png}
#When added to start:
dictionary = {"hello":image3.png}
#new message
dictionary = {"i like py":image1.png, "hello":image3.png}
Is there any way to add the object to the start of the dictionary?
For the use case described it sounds like a list of tuples would be a better data structure.
However, it has been possible to order a dictionary since Python 3.7. Dictionaries are now ordered by insertion order.
To add an element anywhere other than the end of a dictionary, you need to re-create the dictionary and insert the elements in order. This is pretty simple if you want to add an entry to the start of the dictionary.
# Existing data structure
old_dictionary = {"hello": "image3.png"}
# Create a new dictionary with "I like py" at the start, then
# everything from the old data structure.
new_dictionary = {"i like py": "image1.png"}
new_dictionary.update(old_dictionary)
# new_dictionary is now:
# {'i like py': 'image1.png', 'hello': 'image3.png'}
(python3) good example from Manjeet on geeksforgeeks.org
test_dict = {"Gfg" : 5, "is" : 3, "best" : 10}
updict = {"pre1" : 4, "pre2" : 8}
# ** operator for packing and unpacking items in order
res = {**updict, **test_dict}
First of all it doesn't added the item at the end of dictionary because dictionaries use hash-table to storing their elements and are unordered. if you want to preserve the order you can use collections.OrderedDict.but it will appends the item at the end of your dictionary. One way is appending that item to the fist of your items then convert it to an Orderd:
>>> from collections import OrderedDict
>>> d=OrderedDict()
>>> for i,j in [(1,'a'),(2,'b')]:
... d[i]=j
...
>>> d
OrderedDict([(1, 'a'), (2, 'b')])
>>> d=OrderedDict([(3,'t')]+d.items())
>>> d
OrderedDict([(3, 't'), (1, 'a'), (2, 'b')])
Also as another efficient way if it's not necessary to use a dictionary you can use a deque that allows you to append from both side :
>>> from collections import deque
>>> d=deque()
>>> d.append((1,'a'))
>>> d.append((4,'t'))
>>> d
deque([(1, 'a'), (4, 't')])
>>> d.appendleft((8,'p'))
>>> d
deque([(8, 'p'), (1, 'a'), (4, 't')])
I am not sure that a dictionary is the best data structure for your data, but you may find useful collections.OderedDict. It is basically a dictionary that remembers the order of keys added to the dictionary in a FIFO fashion (which is the opposite of what you need).
If you want to retrieve all your items starting from the most recent one, you can use reversed() to reverse dictionary iterators. You can also use the method popitem() to retrieve (and remove) from the dictionary the key-value pair you last entered.
Link to the docs: https://docs.python.org/2/library/collections.html#collections.OrderedDict
As others have pointed out, there is no concept of "order" in a standard dict. Although you can use an OrderedDict to add ordering behavior, this brings other considerations -- unlike a normal dict, isn't a portable data structure (e.g. dumping to JSON then reloading does not preserve order) -- and isn't available in the standard library for all versions of Python.
You might be better off using a sequential key in a standard dict -- a count-up index, or time stamp -- to keep things simple.
As others have pointed out, there is no "order" in a dictionary. However, if you are like me and just need a temporary (/ hacky) workaround that works for your purposes. There is a way to do this.
You can iterate the dictionary, and append the item at the beginning of the iteration process. This seemed to work for me.
The relevant part is where new_fields is declared. I am including the rest for context.
userprofile_json = Path(__file__).parents[2] / f"data/seed-data/user-profile.json"
with open(userprofile_json) as f:
user_profiles = json.load(f)
for user_profile in user_profiles:
new_fields = {
'user': user_profile['fields']['username'],
}
for k, v in user_profile['fields'].items():
new_fields[k] = v
user_profile['fields'] = new_fields
with open(userprofile_json, 'w') as f:
json.dump(user_profiles, f, indent=4)

how is a dictionary sorted?

Ladies and Gents,
I have a question about dictionaries in python. While playing around I noticed something that to me seems strange.
I define a dict like this
stuff={'age':26,'name':'Freddie Mercury', 'ciy':'Vladivostok'}
I then add the word 'first' to stuff like this:
stuff[1]='first'
When I print it out, it's fine
stuff
{1: 'first', 'age': 26, 'name': 'Freddie Mercury', 'city': 'Vladivostok'}
Then I add the word second:
stuff[2]='second'
and that's fine, but when I display the content I get:
stuff
{1: 'first', 'age': 26, 2: 'second', 'name': 'Freddie Mercury', 'city': 'Vladivostok'}
** notice that 2 is now the third element, and not the second (in order) or the first (if elements are added to the beginning) element
And when I add in the third element 'wtf', now all of a sudden everything is back in order and I'm quite confused as to what's going on.
stuff[3]='wtf'
stuff
{1: 'first', 2: 'second', 3: 'wtf', 'name': 'Freddie Mercury', 'age': 26, 'city': 'Vladivostok'}
Could someone please explain to me what's going on here?
The order you get from a dictionary is undefined. You should not rely on it. In this case, it happens to depend on the hash values of the underlying keys, but you shouldn't assume that's always the case.
If order matters to you, use should use an OrderedDict (since Python 2.7):
>>> from collections import OrderedDict
>>> stuff=OrderedDict({'age':26,'name':'Freddie Mercury', 'city':'Vladivostok'})
>>> stuff[1]='first'
>>> print stuff
OrderedDict([('city', 'Vladivostok'), ('age', 26), ('name', 'Freddie Mercury'), (1, 'first')])
>>> stuff[2]='second'
>>> print stuff
OrderedDict([('city', 'Vladivostok'), ('age', 26), ('name', 'Freddie Mercury'), (1, 'first'), (2, 'second')])
>>> stuff[3]='wtf'
>>> print stuff
OrderedDict([('city', 'Vladivostok'), ('age', 26), ('name', 'Freddie Mercury'), (1, 'first'), (2, 'second'), (3, 'wtf')])
Dictionaries are unordered data structures, so you should have no expectations
Learn what a hashtable is: http://en.wikipedia.org/wiki/Hash_table
In short, dict has an internal array, and inserts values at slots chosen through a hash function. The nature of this function is that it spreads the entries around evenly.
There are some (important) guarantees about order. From the docs (Python 2.7.2) http://docs.python.org/library/stdtypes.html#dict.items:
If items(), keys(), values(), iteritems(), iterkeys(), and
itervalues() are called with no intervening modifications to the
dictionary, the lists will directly correspond. This allows the
creation of (value, key) pairs using zip(): pairs = zip(d.values(),
d.keys()). The same relationship holds for the iterkeys() and
itervalues() methods: pairs = zip(d.itervalues(), d.iterkeys())
provides the same value for pairs. Another way to create the same list
is pairs = [(v, k) for (k, v) in d.iteritems()].
In a list, elements are ordered by their index (position in the list).
A dictionary is, for a all intensive purposes, a bag. Things may move around, but you shouldn't be concerned about that. You access items by their keys. You could think of keys as labels, which uniquely identify their values.
stuff = {} # hey python, please create a bag called stuff.
stuff[1]='first' # hey python, please put the string 'first' in my bag called stuff.
# if I ever need to access 'first' from this bag, I will ask for 1
# so please attach the label (key) 1 to the item (value) 'first'
print stuff[1] # hey python, please find the thing in my bag called stuff
# that has a label with 1 attached to it
print stuff[2] # hey python, please find the thing in my bag called stuff
# that has a label called 1 attached to it
Traceback (most recent call last):
File "<stdin>", line 3, in <module>
KeyError: 2 # python says "hey programmer, nothing in your bag called stuff has a label with 2 attached to it
Hope this helps
The build in dict gives no guarantee of order of keys after an insertion or deletion.
If you want to keep insertion order (by time of key insertion/update or by key + value insertion/ update) or sorted by key, use the orderddict package http://anthon.home.xs4all.nl/Python/ordereddict/ that I wrote. It is implemented in C (and thus only works for CPython, but almost as fast as the build in dict).

Categories