How to iterate over a dictionary of tuples?
I have the following tuples month,day
stored in a dictionary seasons
:
seasons = {
'S1': (
((1, 10),(5, 31))
),
'S2': (
((9, 1),(1, 9))
),
'S3': (
((6, 1),(9, 30))
)
}
I want to check what date-time range is in dt
and assign a name S1
, S2
or S3
, respectively.
I tried to do it this way, but start
also end
appear to be numbers instead of tuples.
def getSeason(dt):
season = None
for t, ranges in seasons.items():
for start, end in ranges:
if date(dt.year,start(0),start(1)) <= dt.date() <= date(dt.year,end(0),end(1)):
season = t
break
if season is not None:
break
return season
source to share
Several questions I see with your code -
-
You seem to be assuming that you have a tuple of tuples of tuples, but in reality you have a tuple is dumb because when you try to make a tuple of a single element, you need to follow the element with
,
, otherwise python will interpret it as parentheses. used for grouping. Example -a = ((1,2),)
In a simple case
a = (1,)
-
Second, when accessing the value of a tuple, you should use
start[0]
, notstart(0)
as the latter is trying to call it as a function, with a parameter0
. -
Your logic is not considering the case where the start of the season is one year and the end of the season is next year.
So the seasons look like
seasons = {
'S1': (
((1, 10),(5, 31)),
),
'S2': (
((9, 1),(1, 9)),
),
'S3': (
((6, 1),(9, 30)),
)
}
Small change in your original function getSeason()
to make your case work -
def getSeason(dt):
season = None
for t, ranges in seasons.items():
for start, end in ranges:
if start[0] <= end[0] or (start[0] == end[0] and start[1] <= end[1]):
if date(dt.year,start[0],start[1]) <= dt.date() <= date(dt.year,end[0],end[1]):
season = t
break
else:
if (date(dt.year,start[0],start[1]) <= dt.date() <= date(dt.year+1,end[0],end[1])) or (date(dt.year-1,start[0],start[1]) <= dt.date() <= date(dt.year,end[0],end[1])):
season = t
break
if season is not None:
break
return season
The above will work, but you don't need a tuple of tuples for your use case, you can change the logic in your function getSeasons()
to use a tuple of a tuple.
source to share
The main problem is that you are unpacking the tuple, getting int
in start
and out end
. if you remove the nested loop, your code works as you expect:
from datetime import datetime
seasons = {
'S1': (
(1, 10), (5, 31),
),
'S2': (
(9, 1), (1, 9),
),
'S3': (
(6, 1), (9, 30),
)
}
def getSeason(dt):
for t, (start, end) in seasons.iteritems():
if start[0] < end[0] and datetime(dt.year, start[0], start[1]) <= dt <= datetime(dt.year, end[0], end[1]):
return t
elif (start[0] > end[0] and
(datetime(dt.year, start[0], start[1]) <= dt <= datetime(dt.year + 1, end[0], end[1]) or
datetime(dt.year - 1, start[0], start[1]) <= dt <= datetime(dt.year, end[0], end[1]))):
return t
else:
continue
return None
source to share
Try the following:
from datetime import date
seasons = {
'S1': (
((1, 10),(5, 31))
),
'S2': (
((9, 1),(1, 9))
),
'S3': (
((6, 1),(9, 30))
)
}
def getSeason(dt):
for sname, duration in seasons.items():
start, end = duration
start_date = date(dt.year, start[0], start[1])
end_date = date(dt.year, end[0], end[1])
if dt < start_date:
start_date = date(start_date.year - 1, start_date.month, start_date.day)
end_date = date(end_date.year - 1, end_date.month, end_date.day)
if end_date < start_date:
end_date = date(end_date.year+1, end_date.month, end_date.day)
if start_date <= dt <= end_date:
return sname
return None
if __name__ == '__main__':
s = getSeason(date(1921, 5, 24))
print s
source to share