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).
Related
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())
I have a dictionary currently setup as
{'name': 'firm', 'name':'firm', etc},
Where keys are analyst names and values are analyst firms.
I am trying to create a new dictionary where the new values are the old k,v pairs and the associated key is simply the index (1, 2, 3, 4, etc).
Current code is below:
num_analysts = len(analysts.keys())
for k,v in analysts.items():
analysts_dict = dict.fromkeys(range(num_analysts), [k,v])
Current result
Each numeric key is getting given the same value (old k,v pair). What is wrong with my expression?
You can enumerate the items and convert them to a dictionary. However, dictionaries, in general, are not ordered. This means that the keys may be assigned essentially randomly.
dict(enumerate(analysts.items(), 1))
#{1: ('name1', 'firm1'), 2: ('name2', 'firm2')}
Enumerate and dictionary comprehension for this
d = {'name1': 'firm1', 'name2': 'firm2'}
d2 = {idx: '{}, {}'.format(item, d[item]) for idx, item in enumerate(d, start = 1)}
{1: 'name1, firm1', 2: 'name2, firm2'}
There are already effective answer posted by others. So I may just put the reason why your own solution does't work properly. It may caused by lazy binding. There are good resource on: http://quickinsights.io/python/python-closures-and-late-binding/
Because late binding will literally pick up the last one in dictionary you created. But this last one is not "virtually last one", it is determined by the OS. (Other people already give some explanation on dict data-structure.)
For each time you run in python command line the result may change. If you put the code in .py file, For each time you run in IDE, the result will be same.(always the last one in dict)
During each iteration, analysts_dict is assigned value based on the result of dict.items().
However, you should use comprehension to generate the final result in one line,
E.g. [{i: e} for i, e in enumerate(analysts.items())]
analysts = {
"a": 13,
"b": 123,
"c": 1234
}
num_analysts = len(analysts.keys())
analysts_dict = [{i: e} for i, e in enumerate(analysts.items())]
print(analysts_dict)
>> [{0: ('a', 13)}, {1: ('b', 123)}, {2: ('c', 1234)}]
This code
for k,v in analysts.items():
analysts_dict = dict.fromkeys(range(num_analysts), [k,v])
loops over the original dict and on each loop iteration it creates a new dict using the range numbers as the keys. By the way, every item in that dict shares a reference to a single [k, v] list object. That's generally a bad idea. You should only use an immutable object (eg None, a number, or a string) as the value arg to the dict.fromkeys method. The purpose of the method is to allow you to create a dict with a simple default value for the keys you supply, you can't use it to make a dict with lists as the values if you want those lists to be separate lists.
The new dict object is bound to the name analysts_dict. On the next loop iteration, a new dict is created and bound to that name, replacing the one just created on the previous loop, and the replaced dict is destroyed.
So you end up with an analysts_dict containing a bunch of references to the final [k, v] pair read from the original dict.
To get your desired result, you should use DYZ's code, which I won't repeat here. Note that it stores the old name & firm info in tuples, which is better than using lists for this application.
(Python 2.x) A list of dicts with only unique key-value pairs, sorted alfabetically by name, names are unique as well:
dictlist = [
{'name': 'Monty', 'has': 'eggs'},
{'name': 'Terry', 'has': 'bacon'}
]
I want to get what a given name has, by name. The following works.
names = ['Monty', 'Terry']
print dictlist[names.index('Terry')]['has']
I've made a parallel list with just names in the same order as the names in the dictlist, so I can make use of the order of the list. (I could fill names with a for loop, but that's not relevant here).
From here, among others, I know I could do this:
print next((d['has'] for d in dictlist if d['name'] == 'Terry'), None)
but that's only better if dictlist isn't sorted by name.
So I'm wondering if there isn't a more concise way of doing this, preferably one that's at least as readable as the first method?
I would not use a list at all. I would use a dictionary instead.
dictlist = {
'Monty': {'has': 'eggs'},
'Terry': {'has': 'bacon'}
}
This allows you to look up values by name like so: dictlist['Monty']['has']
If you must use a list then I think you have a good solution as-is.
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.
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)