简体   繁体   中英

How to iterate through a dictionary of tuples?

I have the following tuples of month,day stored in the dictionary seasons :

seasons = {
    'S1': (
        ((1, 10),(5, 31))
    ),
    'S2': (
        ((9, 1),(1, 9))
    ),
    'S3': (
        ((6, 1),(9, 30))
    )
}

I want to check in which date interval a datetime dt is located and assign the name S1 , S2 or S3 , accordingly.

I tried to do it this way, but start and end seem 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

Few issues, i see with your code -

  1. You seem to be assuming you have a tuple of tuple of tuples, but you actually have a tuple of tupe, because when you try to make a tuple of one element, you need to follow the element by a , , otherwise python will interpret it as brackets used for grouping. Example -

     a = ((1,2),) 

    In simple case,

     a = (1,) 
  2. Secondly, when accessing a tuple value, you should use start[0] , not start(0) , as the latter is trying to call it as a function, with 0 as parameter.

  3. Your logic does not consider the case where the season start is in one year, whereas the season end is in the next year.

So seasons would look like -

seasons = {
    'S1': (
        ((1, 10),(5, 31)),
    ),
    'S2': (
        ((9, 1),(1, 9)),
    ),
    'S3': (
        ((6, 1),(9, 30)),
    )
}

A small change to your original getSeason() function, 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 would work, but you don't really need a tuple of tuple of tuples for your use case, you can change the logic in your getSeasons() function to use a tuple of tuple .

The main problem is that you're unpacking the tuple thus getting an int into start and end . if you remove the nested loop your code will work 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

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM