Reputation: 21
Suppose I have a dictionary with this structure:
namedict = {
1880:
[('Mary', 'F', '7065', 1),
('Anna', 'F', '2604', 2),
('John', 'M', '9655', 1)],
1881:
[('Mary', 'F', '8065', 1),
('Anna', 'F', '9604', 2),
('John', 'M', '5655', 1)],
1882:
[('Mary', 'F', '9065', 1),
('Anna', 'F', '9604', 2),
('John', 'M', '5655', 1)]
}
I'd like to transform the dictionary so that the data is in the following list structure:
[{(Mary, F): {1880: [7065, 1], 1881: [8065, 1], 1882: [9065, 1]}]
[{(Anna, F): {1880: [2064, 2], 1881: [9604, 2], 1882: [9604, 1]}]...
Any suggestions as to how I would do this?
Upvotes: 1
Views: 48
Reputation: 280
Based on how I understand the problem, the following code should work. It uses a defaultdict (something I recently started using more, but it is also worth noting that there is another answer that uses it as well )
from collections import defaultdict
def reorder_namedict(namedict):
new_list_representation = defaultdict(dict)
for year in namedict:
for user_tuple in namedict[year]:
# tuple unpacking has changed based on what version of python is used.
# I ran this against 3.4.3 and 2.7.6.
# Use python --version to check if you don't know your version
name, gender, num1, num2 = user_tuple
new_list_representation[str((name, gender,))][year] = [int(num1), int(num2)]
new_list = []
# sorted so that it comes out in consistent order for string matching
for key in sorted(new_list_representation):
new_list.append([{key: new_list_representation[key]}])
return new_list
The code returns a list of lists to match the second code block in the question. The following input comes from the question (plus or minus some formatting) and expected output (from the question, with the small modification to return a list of the desired lists instead of trying to return multiple lists.
namedict = {
1880:
[('Mary', 'F', '7065', 1),
('Anna', 'F', '2604', 2),
('John', 'M', '9655', 1)],
1881:
[('Mary', 'F', '8065', 1),
('Anna', 'F', '9604', 2),
('John', 'M', '5655', 1)],
1882:
[('Mary', 'F', '9065', 1),
('Anna', 'F', '9604', 2),
('John', 'M', '5655', 1)]
}
output_to_match = [
[{str(('Anna', 'F',)): {1880: [2604, 2], 1881: [9604, 2], 1882: [9604, 2],}}],
[{str(('John', 'M',)): {1880: [9655, 1], 1881: [5655, 1], 1882: [5655, 1],}}],
[{str(('Mary', 'F',)): {1880: [7065, 1], 1881: [8065, 1], 1882: [9065, 1],}}],
]
The following code will demonstrate that the two lists are the same. You can uncomment the print statements that show each list and how it is converted to a string, but I find that having the code check that each character matches is more reliable (I missed a few small mistakes myself).
match_this = str(output_to_match)
iTried = str(reorder_namedict(namedict))
# print(match_this)
# print('---')
# print(iTried)
for i in range(0, len(match_this)):
if not match_this[i] == iTried[i]:
print('%d %s %s' % (i, match_this[i], iTried[i],))
print(match_this == iTried)
Upvotes: 0
Reputation: 474221
From what I understand, the collections.defaultdict()
can help in this case:
from collections import defaultdict
from pprint import pprint
namedict = {
1880:
[('Mary', 'F', '7065', 1),
('Anna', 'F', '2604', 2),
('John', 'M', '9655', 1)],
1881:
[('Mary', 'F', '8065', 1),
('Anna', 'F', '9604', 2),
('John', 'M', '5655', 1)],
1882:
[('Mary', 'F', '9065', 1),
('Anna', 'F', '9604', 2),
('John', 'M', '5655', 1)]
}
d = defaultdict(dict)
for key, values in namedict.items():
for name, gender, value1, value2 in values:
d[(name, gender)][key] = [value1, value2]
pprint(dict(d))
Prints:
{('Anna', 'F'): {1880: ['2604', 2], 1881: ['9604', 2], 1882: ['9604', 2]},
('John', 'M'): {1880: ['9655', 1], 1881: ['5655', 1], 1882: ['5655', 1]},
('Mary', 'F'): {1880: ['7065', 1], 1881: ['8065', 1], 1882: ['9065', 1]}}
Or, in case of Python3, you can use the extended iterable unpacking and make the solution a bit more generic:
d = defaultdict(dict)
for key, values in namedict.items():
for name, gender, *values in values:
d[(name, gender)][key] = values
Upvotes: 1