Python Using Databases in Python Our Diary App OrderedDict Practice

Kevin Faust
Kevin Faust
15,352 Points

python challenge help

Hello,

my code doesn't seem to be working and i'm not sure why. I have this same code layout in workspaces and it works fine

menu.py
from collections import OrderedDict

## Menu Items:
# 'n', 'new challenge'
# 's', 'new step'
# 'd', 'delete a challenge'
# 'e', 'edit a challenge'

menu = OrderedDict({

 'n': 'new challenge',
 's': 'new step',
 'd': 'delete a challenge',
 'e': 'edit a challenge',

  })

3 Answers

Chris Freeman
MOD
Chris Freeman
Treehouse Moderator 60,173 Points

The OP code will run because the OrderedDict constructor accepts a single iterable which can be a dictionary. It fails because the order of the key/value pairs from this dictionary is not guaranteed.

>>> menu = OrderedDict({
... 
...  'n': 'new challenge',
...  's': 'new step',
...  'd': 'delete a challenge',
...  'e': 'edit a challenge',
... 
...   })
>>> menu
OrderedDict([('n', 'new challenge'), ('e', 'edit a challenge'), ('s', 'new step'), ('d', 'delete a challenge')])

Simply changing the curly braces to square brackets is not sufficient because that is not legal syntax for a list. Turning each pair in to a tuple will satisfy this:

menu = OrderedDict([
    ( 'n', 'new challenge'),
    ('s', 'new step'),
    ('d', 'delete a challenge'),
    ('e', 'edit a challenge'),
  ])

produces

>>> menu = OrderedDict([
...     ( 'n', 'new challenge'),
...     ('s', 'new step'),
...     ('d', 'delete a challenge'),
...     ('e', 'edit a challenge'),
...   ])
>>> menu
OrderedDict([('n', 'new challenge'), ('s', 'new step'), ('d', 'delete a challenge'), ('e', 'edit a challenge')])

The other post mentioned by Kevin Faust was about alternatives of specifying regular dicts. But thanks for the mention!

Kevin Faust
Kevin Faust
15,352 Points

thank you chris

when you say "It fails because the order of the key/value pairs from this dictionary is not guaranteed.", i dont understand what you mean.

and i still dont understand why my (wrong?) code works in workspaces

so only the tuple list would work in this case?

now that i think about it, i dont really understand what an OrderedDict is. Online, I read that it "remembers the order in which its contents are added." but what does that mean? It's not like everytime I loop through a dictionary, the values will be returned in a different order

no matter how hard i look at it, a normal dict and an ordereddict look the exact same to me

Chris Freeman
Chris Freeman
Treehouse Moderator 60,173 Points

An OrderedDict iterates over it's argument to extract the key/value pairs. When using a dict as an argument, the key value pairs are presented back in an order determined by the hashed value of the key. The order is the same as seen when printing a dict:

>>> d = {"these": 1, "keys": 2, "start": 3, "in":4, "order":5}
>>> print(d)
{'these': 1, 'order': 5, 'keys': 2, 'in': 4, 'start': 3}

Revisiting the ways to specifying a dict from the other post, when a dictionary is presented as an argument the order of the key/value pairs is ordered by the hash. Using the same techniques from the other post:

>>> oa = OrderedDict(n='new challenge', s='new step', d='delete a challenge', e='edit a challenge')
# Skipping 'b'
>>> oc = OrderedDict(zip(['n', 's', 'd', 'e'], ['new challenge', 'new step', 'delete a challenge', 'edit a challenge']))
>>> od = OrderedDict([('n', 'new challenge'), ('s', 'new step'), ('d', 'delete a challenge'), ('e', 'edit a challenge')])
>>> oe = OrderedDict({'n': 'new challenge', 's': 'new step', 'd': 'delete a challenge', 'e': 'edit a challenge'})

>>> oa
OrderedDict([('e', 'edit a challenge'), ('n', 'new challenge'), ('s', 'new step'), ('d', 'delete a challenge')])
>>> oc
OrderedDict([('n', 'new challenge'), ('s', 'new step'), ('d', 'delete a challenge'), ('e', 'edit a challenge')])
>>> od
OrderedDict([('n', 'new challenge'), ('s', 'new step'), ('d', 'delete a challenge'), ('e', 'edit a challenge')])
>>> oe
OrderedDict([('n', 'new challenge'), ('e', 'edit a challenge'), ('s', 'new step'), ('d', 'delete a challenge')])

OrderedDict oc and od work because the argument is a list not a dict.

So one lesson here is that if order of arguments is important, a dictionary won't do.

A dict and an OrderedDictbehave the same when referencing using a key. But the similarity ends there. Notice how when printed, an OrderedDict presents differently and a true dict. If an item is deleted from a dict then replaced the two dicts would remain equal. If an item is removed from an OrderedDict then replaced, the item goes at the "end" of the OrderedDict so they wouldn't not be equal.

Does that answer your questions?

Kevin Faust
Kevin Faust
15,352 Points

thanks alot chris it is starting to make sense

So OrderedDicts are basically a list of tuples --> (key, value) <-- which are treated like a dictionary

normal dicts dont keep their order (as I have just learned) and so we use OrderedDicts if we want order to be kept. I was confused because "OrderedDicts" doesn't contain "actual" dicts but rather tuples which behave like dicts

What does ordering my hash mean?

What did you mean by: "If an item is deleted from a dict then replaced the two dicts would remain equal. If an item is removed from an OrderedDict then replaced, the item goes at the "end" of the OrderedDict so they wouldn't not be equal."?

Chris Freeman
Chris Freeman
Treehouse Moderator 60,173 Points

I would say the print string of an OrderedDict is an list of tuples. Internally, OrderedDict has many methods available: clear, copy, fromkeys, get, items, keys, move_to_end, pop, popitem, setdefault, update, values

"If an item is deleted from a dict then replaced the two dicts would remain equal."

Let's look at how dicts behave:

>>> dict1 = {"these": 1, "keys": 2, "start": 3, "in": 4, "order": 5}
>>> dict2 = {"these": 1, "keys": 2, "start": 3, "in": 4, "order": 5}
>>> dict1 == dict2
True
# remove 'keys' from dict2
>>> del dict2['keys']
>>> dict2
{'these': 1, 'order': 5, 'in': 4, 'start': 3}
>>> dict1 == dict2
False
# add 'keys' back to dict2
>>> dict2['keys'] = 2
>>> dict2
{'these': 1, 'order': 5, 'keys': 2, 'in': 4, 'start': 3}
# The two dicts are again equivalent
>>> dict1 == dict2
True

If an item is removed from an OrderedDict then replaced, the item goes at the 'end' of the OrderedDict so they wouldn't not be equal.

Repeating the same for OrderedDict does not behave the same.

>>> from collections import OrderedDict
>>> orddict1 = OrderedDict([('these', 1), ('keys', 2), ('start', 3), ('in', 4), ('order', 5)])
>>> orddict2 = OrderedDict([('these', 1), ('keys', 2), ('start', 3), ('in', 4), ('order', 5)])
>>> orddict1 == orddict2
True
# remove 'keys' from orddict2
>>> del orddict2['keys']
>>> orddict2
OrderedDict([('these', 1), ('start', 3), ('in', 4), ('order', 5)])
# add 'keys' back to orddict2
>>> orddict2['keys'] = 2
>>> orddict2
OrderedDict([('these', 1), ('start', 3), ('in', 4), ('order', 5), ('keys', 2)])
# The two OrderedDicts are Not equivalent
>>> orddict1 == orddict2
False

Looking at the .keys() order of dict1, dict2, orddict1, and orddict2 shows the iteration order would be different:

# dict1 order based on hash order
>>> list(dict1.keys())
['these', 'order', 'keys', 'in', 'start']
# dict2 uses same hash order
>>> list(dict2.keys())
['these', 'order', 'keys', 'in', 'start']
# orddict1 order based and order of addition
>>> list(orddict1.keys())
['these', 'keys', 'start', 'in', 'order']
# orddict2 is different as 'keys' was added last
>>> list(orddict2.keys())
['these', 'start', 'in', 'order', 'keys']
Kevin Faust
Kevin Faust
15,352 Points

thank you. makes perfect sense now

Steven Parker
Steven Parker
204,019 Points

It works in workspaces? I would think you'd need to put parentheses around each key/value pair, exchange the colons for commas, and change the braces into brackets.

Kevin Faust
Kevin Faust
15,352 Points

yea it works in workspaces. and here was a post from Chris Freeman :

https://teamtreehouse.com/community/why-is-ordereddict-written-like-a-list

e = dict({'three': 3, 'one': 1, 'two': 2})

as you can see, what i entered in the solution and in workspaces is exactly that. yet for some reason the code challenge doesnt pass me

i know that i can pass if i use the following way:

from collections import OrderedDict

menu = OrderedDict([

 ('n': 'new challenge'),
 ('s': 'new step'),
 ('d': 'delete a challenge'),
 ('e': 'edit a challenge'),

  ])

but i found that way more harder to understand because we are creating a list of tuples and then changing that to a dict

Steven Parker
Steven Parker
204,019 Points

I don't think that will pass the challenge. But it will if you change the colons into commas.

Chris certainly knows his Python. But it could be one of those cases where the challenge is looking specifically for a method that was introduced in the course, and something more advanced might not be recognized.

Steven Parker
Steven Parker
204,019 Points

Hey I found this:

"The OrderedDict constructor and update() method both accept keyword arguments, but their order is lost because Python’s function call semantics pass-in keyword arguments using a regular unordered dictionary"

That post you cited before was discussing regular dict's.

Kevin Faust
Kevin Faust
15,352 Points

i also finally realize why this worked:

menu = OrderedDict({
  'a': add_entry,
  'v': view_entries
  })

it gave off the false impression that this syntax was right because the hash ordering happened to keep the two keys in their right order. i added another key and i saw that no longer were the keys in order