Pythonic way to convert a list of integers into a string of comma-separated ranges

后端 未结 7 798
感情败类
感情败类 2020-12-01 05:06

I have a list of integers which I need to parse into a string of ranges.

For example:

 [0, 1, 2, 3] -> \"0-3\"
 [0, 1, 2, 4, 8] -> \"0-2,4,8\"
         


        
7条回答
  •  挽巷
    挽巷 (楼主)
    2020-12-01 05:15

    To concatenate strings you should use ','.join. This removes the 2nd loop.

    def createRangeString(zones):
            rangeIdx = 0
            ranges   = [[zones[0], zones[0]]]
            for zone in list(zones):
                if ranges[rangeIdx][1] in (zone, zone-1):
                    ranges[rangeIdx][1] = zone
                else:
                    ranges.append([zone, zone])
                    rangeIdx += 1
    
           return ','.join(
                    map(
                      lambda p: '%s-%s'%tuple(p) if p[0] != p[1] else str(p[0]),
                      ranges
                    )
                  )
    

    Although I prefer a more generic approach:

    from itertools import groupby
    
    # auxiliary functor to allow groupby to compare by adjacent elements.
    class cmp_to_groupby_key(object):
      def __init__(self, f):
        self.f = f
        self.uninitialized = True
      def __call__(self, newv):
        if self.uninitialized or not self.f(self.oldv, newv):
          self.curkey = newv
          self.uninitialized = False
        self.oldv = newv
        return self.curkey
    
    # returns the first and last element of an iterable with O(1) memory.
    def first_and_last(iterable):
      first = next(iterable)
      last = first
      for i in iterable:
        last = i
      return (first, last)
    
    # convert groups into list of range strings
    def create_range_string_from_groups(groups):
      for _, g in groups:
        first, last = first_and_last(g)
        if first != last:
          yield "{0}-{1}".format(first, last)
        else:
          yield str(first)
    
    def create_range_string(zones):
      groups = groupby(zones, cmp_to_groupby_key(lambda a,b: b-a<=1))
      return ','.join(create_range_string_from_groups(groups))
    
    assert create_range_string([0,1,2,3]) == '0-3'
    assert create_range_string([0, 1, 2, 4, 8]) == '0-2,4,8'
    assert create_range_string([1,2,3,4,6,7,8,9,12,13,19,20,22,22,22,23,40,44]) == '1-4,6-9,12-13,19-20,22-23,40,44'
    

提交回复
热议问题