4
\$\begingroup\$

What is a Pythonic way to construct intervals from a sequence of integer numbers? E.g. sequence [1, 2, 3, 6, 7, 9] could be converted to ['1-3', '6-7', '9']. I have implemented it like this:

def sequence_to_intervals(seq):
    intervals = []
    interval_start = None
    interval_end = None
    for i in sorted(seq):
        if interval_start is None:
            interval_start = i
            interval_end = i
        elif interval_end + 1 >= i:
            interval_end = i
        else:
            if interval_start == interval_end:
                intervals.append(str(interval_start))
            else:
                intervals.append(f'{interval_start}-{interval_end}')
            interval_start = i
            interval_end = i
    else:
        if interval_start == interval_end:
            intervals.append(str(interval_start))
        else:
            intervals.append(f'{interval_start}-{interval_end}')
    return intervals

Simple test:

seq = [1, 2, 3, 6, 7, 9]
sequence_to_intervals(seq)
['1-3', '6-7', '9']

Converting intervals back to sequence:

def intervals_to_sequence(intervals):
    seq = []
    for interval in intervals:
        if '-' in interval:
            start, stop = interval.split('-')
            seq += list(range(int(start), int(stop) + 1))
        else:
            seq += [int(interval)]
    return seq

Simple test:

intervals = ['1-3', '6-7', '9']
intervals_to_sequence(intervals)
[1, 2, 3, 6, 7, 9]
\$\endgroup\$
3
  • \$\begingroup\$ interval_end is None is never true. \$\endgroup\$
    – bipll
    May 11 '19 at 6:48
  • \$\begingroup\$ @bipll Good remark. Should I remove this check from my question? \$\endgroup\$
    – niekas
    May 11 '19 at 8:42
  • \$\begingroup\$ Yes, makes sense. \$\endgroup\$
    – bipll
    May 11 '19 at 8:58
1
\$\begingroup\$

For the first part of making sequence to intervals, you could do this instead (prob. more Pythonic?) (This can be converted to a function easier - leave for the exercise)

>>> # if x-1 not in seq:  x becomes start point
>>> # if y+1 not in seq:  y becomes end point
>>> starts = [x for x in seq if x-1 not in seq]
>>> ends =  [y for y in seq if y+1 not in seq]
>>> ends
[3, 7, 9]
>>> intervals = [str(a)+'-'+str(b) for a, b in zip(starts, ends)]
>>> intervals
['1-3', '6-7', '9-9']
>>> 

[Note - last interval could be 'fixed' too - if we see start_point == end_point. Leave as an exercise.]

Then for the second part, it would be quite similar:

>>> seq = []
>>> for inter in intervals:
    s, e = inter.split('-')
    seq += list(range(int(s), int(e)+1))
>>> seq
[1, 2, 3, 6, 7, 9]
\$\endgroup\$
2
  • 1
    \$\begingroup\$ Please read a couple of well received answers "in tags" that interest you, then revisit How do I write a Good Answer? to get the knack of what CodeReview@SE answers should be about - as with your first answer, your observation is about the problem where it should be about the code put up for review. \$\endgroup\$
    – greybeard
    Dec 8 '20 at 7:51
  • \$\begingroup\$ (When presenting code, "ready for cut, paste&run" has its advantages. Granted, "one of the conventional prompts" boosts confidence this is cut&pasted from an interactive session and actually works as far as demonstrated. Which made me wonder how interval '9' could have passed and notice your alternative solution uses a deviating, but not erroneous format.) \$\endgroup\$
    – greybeard
    Dec 8 '20 at 7:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.