Unexpected Data Assignment in Python Nested Dictionaries - python

I am new to python so please excuse any dumb mistakes but after research, I can't figure this out. I am creating a dictionary from a list of days in the month taken from calendar. I had originally used dict.fromkeys() but found this submission that convinced me to change to the dictionary comprehension statement I have. Then I give each value in the dictionary another dictionary that has the day of the week as the key and another dictionary as the value. This dictionary is taskDic which has chores as the keys and will hold people's names as the values.
My problem is that my last statement in my loops is assigning the same person to do trash (etc) for every day even though the loop is just on the first day. I believe there is something wrong with how I start the dictionary because it is assigning values to all the dics as if they are the same.
Basically I have the same problem as the linked issue above but with nested dictionaries. Please let me know if I need to clarify anything. Thank you!
import calendar
week = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']
taskDic = {'Trash':[], 'Groceries':[], 'Clean':[]}
teamList = ['Jane', 'Peter', 'Jake', 'Eliza', 'Sarah', 'Bill']
person = 0
cal = list(calendar.Calendar().itermonthdays(2015, 8))
cal = {k: {} for k in cal}
for i in cal:
cal[i] = {week[i % 7]: taskDic}
for i in cal:
if (cal[i].keys() != 'Saturday') and (cal[i].keys() != 'Sunday'):
for j in cal[i]:
for k in cal[i][j]:
cal[i][j][k] = teamList[person % len(teamList)]
person += 1
My result looks like this:
0 {'Monday': {'Trash': 'Eliza', 'Groceries': 'Sarah', 'Clean': 'Bill'}}
1 {'Tuesday': {'Trash': 'Eliza', 'Groceries': 'Sarah', 'Clean': 'Bill'}}
2 {'Wednesday': {'Trash': 'Eliza', 'Groceries': 'Sarah', 'Clean': 'Bill'}}
3 {'Thursday': {'Trash': 'Eliza', 'Groceries': 'Sarah', 'Clean': 'Bill'}}
etc...

the problem is here:
for i in cal:
cal[i] = {week[i % 7]: taskDic}
you're using the same copy of taskDic every day.
as commented on by #jojonas, a fix is this:
cal[i] = {week[i % 7]: taskDic.copy()}
the only problem with this is that you'll end up creating a bunch of unused copies, which is fine for this task.
what you want to do is something like:
week_of_tasks = [taskDic.copy() for _ in week]
cal[i] = {week[i % 7]: week_of_tasks[i % 7]}

Related

Nested Dictionary Creation - Python

I have a dictionary setup like this:
company = {'Honda': {}
,'Toyota':{}
,'Ford':{}
}
I have a list containing data like this:
years = ['Year 1', 'Year 2', 'Year 3']
Finally, I also have a list of lists containing data like this:
sales = [[55,9,90],[44,22,67],[83,13,91]]
I am trying to achieve a final result that looks like this:
{'Honda': {'Year 1':55,'Year 2':9,'Year 3':90}
,'Toyota':{'Year 1':44,'Year 2':22,'Year 3':67}
,'Ford':{'Year 1':83,'Year 2':13,'Year 3':91}
}
I can access the sub-list if sales like this:
for i in sales:
for j in i:
#this would give me a flat list of all sales
I can't seem to wrap my head around constructing the final dictionary that would tie everything together.
Any help is appreciated!
You can use a dict comprehension with zip.
res = {k : dict(zip(years, sale)) for k, sale in zip(company, sales)}
You can use zip to pair corresponding information together. First, zip the brand names with the values in sales. Next, zip years with a particular brand's sales numbers.
company = {brand: dict(zip(years, sales_nums))
for brand, sales_nums in zip(["Honda", "Toyota", "Ford"], sales)}
You can use zip and a double for-loop to zip all 3 lists. Here you are:
final_dict = {}
for i, (name, sub_dict) in enumerate(company.items()):
for year, sale in zip(years, sales[i]):
sub_dict[year] = sale
final_dict[name] = sub_dict

Is there a way to sort a dictionary from the outside in

I'm trying to create an event manager in which a dictionary stores the events like this
my_dict = {'2020':
{'9': {'8': ['School ']},
'11': {'13': ['Doctors ']},
'8': {'31': ['Interview']}
},
'2021': {}}
In which the outer key is the year the middle key is a month and the most inner key is a date which leads to a list of events.
I'm trying to first sort it so that the months are in order then sort it again so that the days are in order. Thanks in advance
Use-case
DevOrangeCrush wishes to sort on keys in a nested dictionary where the nesting occurs on multiple levels
Solution
Normalize the data so that the dates match ISO8601 format, for easier sorting
In plain English, this means make sure you always use two digits for month and date, and always use four digits for year
Re-normalize the original dictionary data structure into a single list of dictionaries, where each dictionary represents a row, and the list represents an outer containing table
this is known as an Array of Hashes in perl-speak
this is known as a list of objects in JSON-speak
Once your data is restructured you are solving a much more well-known, well-documented, and more obvious problem, how to sort a simple list of dictionaries (which is already documented in the See also section of this answer).
Example
import pprint
## original data is formatted as a nested dictionary, which is clumsy
my_dict = {'2020':
{'9': {'8': ['School ']}, '11':
{'13': ['Doctors ']},'8':
{'31': ['Interview']}}, '2021': {}
}
## we want the data formatted as a standard table (aka list of dictionary)
## this is the most common format for this kind of data as you would see in
## databases and spreadsheets
mydata_table = []
ddtemp = dict()
for year in my_dict:
for month in my_dict[year].keys():
ddtemp['month'] = '{0:02d}'.format(*[int(month)])
ddtemp['year'] = year
for day in my_dict[year][month].keys():
ddtemp['day'] = '{0:02d}'.format(*[int(day)])
mydata_row = dict()
mydata_row['year'] = '{year}'.format(**ddtemp)
mydata_row['month'] = '{month}'.format(**ddtemp)
mydata_row['day'] = '{day}'.format(**ddtemp)
mydata_row['task_list'] = my_dict[year][month][day]
mydata_row['date'] = '{year}-{month}-{day}'.format(**ddtemp)
mydata_table.append(mydata_row)
pass
pass
pass
## output result is now easily sorted and there is no data loss
## you will have to modify this if you want to deal with years that
## do not have any associated task_list data
pprint.pprint(mydata_table)
'''
## now we have something that can be sorted using well-known python idioms
## and easily manipulated using data-table semantics
## (search, sort, filter-by, group-by, select, project ... etc)
[
{'date': '2020-09-08','day': '08',
'month': '09','task_list': ['School '],'year': '2020'},
{'date': '2020-11-13','day': '13',
'month': '11','task_list': ['Doctors '],'year': '2020'},
{'date': '2020-08-31','day': '31',
'month': '08','task_list': ['Interview'],'year': '2020'},
]
'''
See also
How to sort a python list-of-dictionary
How to sort objects by multiple keys
Why you should use ISO8601 date format
ISO8601 vs timestamp
To get sorted events data, you can do something like this:
def sort_events(my_dict):
new_events_data = dict()
for year, month_data in my_dict.items():
new_month_data = dict()
for month, day_data in month_data.items():
sorted_day_data = sorted(day_data.items(), key=lambda kv: int(kv[0]))
new_month_data[month] = OrderedDict(sorted_day_data)
sorted_months_data = sorted(new_month_data.items(), key=lambda kv: int(kv[0]))
new_events_data[year] = OrderedDict(sorted_months_data)
return new_events_data
Output:
{'2020': OrderedDict([('8', OrderedDict([('31', ['Interview'])])),
('9', OrderedDict([('8', ['School '])])),
('11', OrderedDict([('13', ['Doctors '])]))]),
'2021': OrderedDict()}
A simple dict can't be ordered, you could do it using a OrderedDict but if you simply need to get it sorted while iterating on it do like this
for year in sorted(map(int, my_dict)):
year_dict = my_dict[str(year)]
for month in sorted(map(int, year_dict)):
month_dict = year_dict[str(month)]
for day in sorted(map(int, month_dict)):
events = month_dict[str(day)]
for event in events:
print(year, month, day, event)
Online Demo
The conversion to int is to ensure right ordering between the numbers, without you'll get 1, 10, 11, .., 2, 20, 21
A dictionary in Python does not have an order, you might want to try the OrderedDict class from the collections Module which remembers the order of insertion.
Of course you would have to sort and reinsert the elements whenever you insert a new element which should be placed before any of the existing elements.
If you care about order maybe a different data structure works better. For example a list of lists.

How to create a Pandas DataFrame from a list of OrderedDicts?

I have the following list:
o_dict_list = [(OrderedDict([('StreetNamePreType', 'ROAD'), ('StreetName', 'Coffee')]), 'Ambiguous'),
(OrderedDict([('StreetNamePreType', 'AVENUE'), ('StreetName', 'Washington')]), 'Ambiguous'),
(OrderedDict([('StreetNamePreType', 'ROAD'), ('StreetName', 'Quartz')]), 'Ambiguous')]
And like the title says, I am trying to take this list and create a pandas dataframe where the columns are: 'StreetNamePreType' and 'StreetName' and the rows contain the corresponding values for each key in the OrderedDict.
I have done some searching on StackOverflow to get some guidance on how to create a dataframe, see here but I am getting an error when I run this code (I am trying to replicate what is going on in that response).
from collections import Counter, OrderedDict
import pandas as pd
col = Counter()
for k in o_dict_list:
col.update(k)
df = pd.DataFrame([k.values() for k in o_dict_list], columns = col.keys())
When I run this code, the error I get is: TypeError: unhashable type: 'OrderedDict'
I looked up this error, here, I get that there is a problem with the datatypes, but I, unfortunately, I don't know enough about the inner workings of Python/Pandas to resolve this problem on my own.
I suspect that my list of OrderedDict is not exactly the same as in here which is why I am not getting my code to work. More specifically, I believe I have a list of sets, and each element contains an OrderedDict. The example, that I have linked to here seems to be a true list of OrderedDicts.
Again, I don't know enough about the inner workings of Python/Pandas to resolve this problem on my own and am looking for help.
I would use list comprehension to do this as follows.
pd.DataFrame([o_dict_list[i][0] for i, j in enumerate(o_dict_list)])
See the output below.
StreetNamePreType StreetName
0 ROAD Coffee
1 AVENUE Washington
2 ROAD Quartz
extracting the OrderedDict objects from your list and then use pd.Dataframe should work
values= []
for i in range(len(o_dict_list)):
values.append(o_dict_list[i][0])
pd.DataFrame(values)
StreetNamePreType StreetName
0 ROAD Coffee
1 AVENUE Washington
2 ROAD Quartz
d = [{'points': 50, 'time': '5:00', 'year': 2010},
{'points': 25, 'time': '6:00', 'month': "february"},
{'points':90, 'time': '9:00', 'month': 'january'},
{'points_h1':20, 'month': 'june'}]
pd.DataFrame(d)

Searching a list of lists?

I have a list of lists called people in python
people = [['10000', '2018-02-04', 'Park', 'Chan'], ['10047', '2018-05-09', 'Tuckwell', 'Luke'], ['10207', '2018-05-06', 'Trentham', 'Sam'], ['10207', '2018-05-06', 'Smith', 'Tristin'], ['10511', '2018-02-07', 'Cotton', 'Marco'], ['10763', '2018-03-07', 'Wideman', 'Jocelyn'], ['10804', '2018-05-09', 'Hamm', 'Megan']]
Each individual list contains an ID, Expiry Date, Last Name, First Name.
What is the best way to go about defining a function that searches 'people' for a desired ID input and returns whether the expiry date of that person is passed today's date or not?
Thanks!
If you can it is better to convert your list of lists to a dictionary where the key is the ID, this way the searches (when performed by ID) will be more efficient.
One way is to use a dictionary to structure your data, mapping id to detail. Another improvement is to store your dates as datetime objects.
from datetime import datetime
people = [['10000', '2018-02-04', 'Park', 'Chan'], ['10047', '2018-05-09', 'Tuckwell', 'Luke'],
['10207', '2018-05-06', 'Trentham', 'Sam'], ['10207', '2018-05-06', 'Smith', 'Tristin'],
['10511', '2018-02-07', 'Cotton', 'Marco'], ['10763', '2018-03-07', 'Wideman', 'Jocelyn'],
['10804', '2018-05-09', 'Hamm', 'Megan']]
# create a dictionary mapping, convert dates to datetime objects
d = {k: [datetime.strptime(v[0], '%Y-%m-%d'), v[1], v[2]] for k, *v in people}
# function to calculate whether date has passed for given id
def return_date_passed(d, i):
return d[i][0] < datetime.now()
res = return_date_passed(d, '10000') # True
res = return_date_passed(d, '10207') # False

Django Copy multiple items from DB Query to dict

i have a Problem with Django and dicts. I want to get only items that match a sting as below. but i canĀ“t get it to work. Thanks for your help.
django_db_query = [{'time': '13:00 Uhr', 'titel': 'test1'}, {'time': '14:00 blah', 'titel': 'test2'}, {'time': '13:00 Uhr', 'titel': 'test3'},]
all_db_items = Django_db.objects.all()
only_13 = dict()
for item in all_db_items:
if item.time is "13":
only_13 += item
wanted: The datastructure and multiple values from my db in my dict but only with time 13:00 Uhr
for item in only_13:
print item.titel
console
test1
test2
Assuming your DjangoDbModel looks something like
def DjangoDbModel(models.Model):
time = models.DateTimeField()
title = models.CharField(max_length=256)
All you need to do in that case is
DjangoDbModels.objects.filter(time__hour=13) if you want to have only items that are from hour 13. You can apply similar filters to the day, year and month for example.
for item in all_db_items:
if item['time'] == "13:00 Uhr":
only_13.update(item)
is only works with identical operands
+ is not implemented for dicts, use update instead
more resources:
https://www.python-course.eu/dictionaries.php

Categories

Resources