Building a dictionary first with useful names helps to understand what is going on.
temp = {}
for year1, values1 in data.items():
for author1, values2 in values1.items():
for number, values3 in values2.items():
temp.setdefault('Year1', []).append(year1)
temp.setdefault('Author1', []).append(author1)
temp.setdefault('No.', []).append(number)
for key, value in values3.items():
temp.setdefault(key, []).append(value)
print(pd.DataFrame(temp))
Output:
Author Author1 City No. \
0 Barack Obama Barack Obama [] 1
1 Barack Obama Barack Obama [Springfield] 2
2 Barack Obama Barack Obama [Chicago] 3
3 Bill Richardson Bill Richardson [] 1
4 Bill Richardson Bill Richardson [] 2
5 Bill Richardson Bill Richardson [] 3
6 Barack Obama Barack Obama [Parma] 1
7 Barack Obama Barack Obama [Sandusky] 2
8 Barack Obama Barack Obama [] 3
Title Type Year Year1
0 Keynote Address at the 2004 Democratic Nationa... address 2008 2008
1 Remarks Announcing Candidacy for President in ... remarks 2008 2008
2 Remarks at the AIPAC Policy Forum in Chicago remarks 2008 2008
3 Iraq Speech to New Hampshire Democratic State ... speech 2008 2008
4 Address to the DNC Winter Meeting address 2008 2008
5 Speech: The New Realism and the Rebirth of Ame... speech 2008 2008
6 535 - Remarks at a Campaign Rally in Parma, Ohio remarks 2012 2012
7 534 - Remarks at a Campaign Rally in Sandusky,... remarks 2012 2012
8 533 - Remarks at a Campaign Rally in Maumee, Ohio remarks 2012 2012
Our create with your desired column order:
df = pd.DataFrame(temp, columns=['Year1', 'Author1', 'No.', 'Author',
'City', 'Title', 'Type', 'Year'])
df

Building a dictionary first with useful names helps to understand what is going on.
temp = {}
for year1, values1 in data.items():
for author1, values2 in values1.items():
for number, values3 in values2.items():
temp.setdefault('Year1', []).append(year1)
temp.setdefault('Author1', []).append(author1)
temp.setdefault('No.', []).append(number)
for key, value in values3.items():
temp.setdefault(key, []).append(value)
print(pd.DataFrame(temp))
Output:
Author Author1 City No. \
0 Barack Obama Barack Obama [] 1
1 Barack Obama Barack Obama [Springfield] 2
2 Barack Obama Barack Obama [Chicago] 3
3 Bill Richardson Bill Richardson [] 1
4 Bill Richardson Bill Richardson [] 2
5 Bill Richardson Bill Richardson [] 3
6 Barack Obama Barack Obama [Parma] 1
7 Barack Obama Barack Obama [Sandusky] 2
8 Barack Obama Barack Obama [] 3
Title Type Year Year1
0 Keynote Address at the 2004 Democratic Nationa... address 2008 2008
1 Remarks Announcing Candidacy for President in ... remarks 2008 2008
2 Remarks at the AIPAC Policy Forum in Chicago remarks 2008 2008
3 Iraq Speech to New Hampshire Democratic State ... speech 2008 2008
4 Address to the DNC Winter Meeting address 2008 2008
5 Speech: The New Realism and the Rebirth of Ame... speech 2008 2008
6 535 - Remarks at a Campaign Rally in Parma, Ohio remarks 2012 2012
7 534 - Remarks at a Campaign Rally in Sandusky,... remarks 2012 2012
8 533 - Remarks at a Campaign Rally in Maumee, Ohio remarks 2012 2012
Our create with your desired column order:
df = pd.DataFrame(temp, columns=['Year1', 'Author1', 'No.', 'Author',
'City', 'Title', 'Type', 'Year'])
df

Using list comprehension:
df = pd.DataFrame([[k, j, n] + [p for p in m.values()] for k, i in d.items() for j, l in i.items() for n, m in l.items()],columns=['Year', 'Author1', 'No.', 'Author', 'City', 'Title', 'Type', 'Year'])
# df
# Year Author1 No. Author City Title Type Year
# 0 2008 Barack Obama 1 Barack Obama [] Keynote Address at the 2004 Democratic Nationa... address 2008
# 1 2008 Barack Obama 2 Barack Obama [Springfield] Remarks Announcing Candidacy for President in ... remarks 2008
# 2 2008 Barack Obama 3 Barack Obama [Chicago] Remarks at the AIPAC Policy Forum in Chicago remarks 2008
# 3 2008 Bill Richardson 1 Bill Richardson [] Iraq Speech to New Hampshire Democratic State ... speech 2008
# 4 2008 Bill Richardson 2 Bill Richardson [] Address to the DNC Winter Meeting address 2008
# 5 2008 Bill Richardson 3 Bill Richardson [] Speech: The New Realism and the Rebirth of Ame... speech 2008
# 6 2012 Barack Obama 1 Barack Obama [Parma] 535 - Remarks at a Campaign Rally in Parma, Ohio remarks 2012
# 7 2012 Barack Obama 2 Barack Obama [Sandusky] 534 - Remarks at a Campaign Rally in Sandusky,... remarks 2012
# 8 2012 Barack Obama 3 Barack Obama [] 533 - Remarks at a Campaign Rally in Maumee, Ohio remarks 2012
I've written this after I saw @VoNWooDSoN's answer. I turned it into an iterator instead of printing inside the function and a little bit of changes to make it more readable. So see his original answer here.
def flatten(d, base=()):
for k, v in d.items():
if isinstance(v, dict):
yield from flatten(v, base + (k,))
else:
yield base + (k, v)
1- yielding instead of printing.
2- isinstance() instead of type so that subclasses of dict can also work. You could also use MutableMapping from typing module instead of dict to make it more generic.
3- IMO , getting (k, v) pairs from .items() is much more readable than k and d[k].
More generic ?
Do you wanna expand this to even more generic which CAN(not have to, like the solution in the OP) accept the number of depths just in case?
Consider these examples:
d_level1 = {"a": 1, "b": 2, "c": 3}
d_level2 = {"group_1": {"a": 1}, "group_2": {"b": 2, "c": 3}}
d_level3 = {"collection_1": d_level2}
for items in flatten(d_level3):
print(items)
print('------------------------------')
for items in flatten(d_level3, depth=0):
print(items)
print('------------------------------')
for items in flatten(d_level3, depth=1):
print(items)
print('------------------------------')
for items in flatten(d_level3, depth=2):
print(items)
output:
('collection_1', 'group_1', 'a', 1)
('collection_1', 'group_2', 'b', 2)
('collection_1', 'group_2', 'c', 3)
------------------------------
('collection_1', {'group_1': {'a': 1}, 'group_2': {'b': 2, 'c': 3}})
------------------------------
('collection_1', 'group_1', {'a': 1})
('collection_1', 'group_2', {'b': 2, 'c': 3})
------------------------------
('collection_1', 'group_1', 'a', 1)
('collection_1', 'group_2', 'b', 2)
('collection_1', 'group_2', 'c', 3)
depth=None doesn't consider the depth (still works like you want at the first place). But now by specifying depths from 0 to 2 you can see that we are able to iterate how deep we want. here is the code:
def flatten(d, base=(), depth=None):
for k, v in d.items():
if not isinstance(v, dict):
yield base + (k, v)
else:
if depth is None:
yield from flatten(v, base + (k,))
else:
if depth == 0:
yield base + (k, v)
else:
yield from flatten(v, base + (k,), depth - 1)
Here's a quick and dirty solution for you:
d_level1 = {"a":1,"b":2,"c":3}
d_level2 = {"group_1":{"a":1}, "group_2":{"b":2,"c":3}}
d_level3 = {"collection_1":d_level2}
def flatten(d_in, base=()):
for k in d_in:
if type(d_in[k]) == dict:
flatten(d_in[k], base+(k,))
else:
print(base + (k, d_in[k]))
flatten(d_level1)
# ('a', 1)
# ('b', 2)
# ('c', 3)
flatten(d_level2)
#('group_1', 'a', 1)
#('group_2', 'b', 2)
#('group_2', 'c', 3)
flatten(d_level3)
# ('collection_1', 'group_1', 'a', 1)
# ('collection_1', 'group_2', 'b', 2)
# ('collection_1', 'group_2', 'c', 3)
Be aware!! Python has a recursion limit of about 1000! So, when using recursion in python think very carefully what you're trying to do and be prepared to catch a RuntimeError if you call a recursive function like this.
EDIT: With comments I realized that I'd made a mistake where I did not add the key to the level1 dict output and that I was using a mutable structure as a default argument. I added these and parens in the print statement and reposted. The output now matches the OP's desired output and uses better and modern python.
You are iterating over cost as well. Try this:
#level one of the dictionary
for drink, data in MENU.items():
#this should print the drinks and cost
print(drink, data["cost"])
#this should print the ingredients
for ingredient in data["ingredients"].items():
print(ingredient)
As Bharel suggested, if you just iterate the dict MENU python will by default iterate only the keys - this is why the code works if you just run:
for drink in MENU:
#this should print the drinks and cost
print(drink)
But it will raise an exception when you try the second for loop, because you are not iterating the values, to iterate both values and keys use MENU.items() as Bharel suggested.
for key, value in MENU.items():
#this should print the drinks and cost
print(key, value["cost"])
#this should print the ingredients
for key_i, value_i in value["ingredients"].items():
print(key_i, value_i)