views:

948

answers:

4

Is there an elegant and pythonic way to trap the first and last item in a for loop which is iterating over a generator?

from calendar import Calendar

cal = Calendar(6)
month_dates = cal.itermonthdates(year, month)
for date in month_dates:
    if (is first item):     # this is fake
        month_start = date
    if (is last item):      # so is this
        month_end = date

This code is attempting to get the first day of the week the month ends on, and the last day of the week the month ends on. Example: for June, month-start should evaluate to 5/31/09. Even though it's technically a day in May, it's the first day of the week that June begins on.

Month-dates is a generator so i can't do the [:-1] thing. What's a better way to handle this?

+7  A: 

How about this?

for i, date in enumerate(month_dates):
    if i == 0:
        month_start = date

month_end = date

enumerate() lets you find the first one, and the date variable falls out of the loop to give you the last one.

RichieHindle
+7  A: 

I would just force it into a list at the beginning:

from calendar import Calendar, SUNDAY

cal = Calendar(SUNDAY)
month_dates = list(cal.itermonthdates(year, month))

month_start = month_dates[0]
month_end = month_dates[-1]

Since there can only be 42 days (counting leading and tailing context), this has negligible performance impact.

Also, using SUNDAY is better than a magic number.

Matthew Flaschen
+1, assuming you change month_end to month_dates[-1] (no colon)
Triptych
Thanks, Triptych.
Matthew Flaschen
Very small point of nitpickiness: As the OP alludes to, itermonthdates actually gives you the dates on a printed calendar, complete with fillers at the beginning and end to make 7-day weeks. Therefore the maximum size of the list for one month is 42. (I know, the memory capacity of modern machines strains to handle the extra 11 dates. ;)
John Y
Good point, John.
Matthew Flaschen
+1 for the extra SUNDAY tip.
T. Stone
+1  A: 

For this specific problem, I think I would go with Matthew Flaschen's solution. It seems the most straightforward to me.

If your question is meant to be taken more generally, though, for any generator (with an unknown and possibly large number of elements), then something more like RichieHindle's approach may be better. My slight modification on his solution is not to enumerate and test for element 0, but rather just grab the first element explicitly:

month_dates = cal.itermonthdates(year, month)
month_start = month_dates.next()
for date in month_dates:
    pass
month_end = date
John Y
+3  A: 

Richie's got the right idea. Simpler:

month_dates = cal.itermonthdates(year, month)
month_start = month_dates.next()
for month_end in month_dates: pass # bletcherous
Tom Anderson
+1 I like it. Seems "Pythonish" (which probably converges to "Pythonic")
David Zaslavsky
clever use of the for variable
T. Stone