intspan / intspan.py

import sys, copy
from itertools import groupby, count

_PY3 = sys.version_info[0] > 2
if _PY3:
    basestring = str
        
class intspan(set):
    def __init__(self, initial=None):
        super(intspan, self).__init__()
        if initial:
            self.update(initial)
            
    def copy(self):
        return copy.copy(self)
        
    def update(self, items):
        super(intspan, self).update(self._parse_range(items))
        
    def discard(self, items):
        for item in self._parse_range(items):
            super(intspan, self).discard(item)
            
    def remove(self, items):
        for item in self._parse_range(items):
            super(intspan, self).remove(item)
            
    def add(self, items):
        for item in self._parse_range(items):
            super(intspan, self).add(item)
    
    def issubset(self, items):
        return super(intspan, self).issubset(self._parse_range(items))
    
    def issuperset(self, items):
        return super(intspan, self).issuperset(self._parse_range(items))
    
    def union(self, items):
        return intspan(super(intspan, self).union(self._parse_range(items)))

    def intersection(self, items):
        return intspan(super(intspan, self).intersection(self._parse_range(items)))
    
    def difference(self, items):
        return intspan(super(intspan, self).difference(self._parse_range(items)))
    
    def symmetric_difference(self, items):
        return intspan(super(intspan, self).symmetric_difference(self._parse_range(items)))

    __le__  = issubset
    __ge__  = issuperset 
    __or__  = union
    __and__ = intersection
    __sub__ = difference
    __xor__ = symmetric_difference
    
    def __ior__(self, items):
        return super(intspan, self).__ior__(self._parse_range(items))
        
    def __iand__(self, items):
        return super(intspan, self).__iand__(self._parse_range(items))

    def __isub__(self, items):
        return super(intspan, self).__isub__(self._parse_range(items))
        
    def __ixor__(self, items):
        return super(intspan, self).__ixor__(self._parse_range(items))
    
    def __eq__(self, items):
        return super(intspan, self).__eq__(self._parse_range(items))
        
    def __iter__(self):
        """
        Iterate in ascending order.
        """
        return iter(sorted(super(intspan, self).__iter__()))
    
    @staticmethod
    def _parse_range(datum):
        if isinstance(datum, basestring):
            result = []
            for part in datum.split(','):
                if '-' in part:
                    start, stop = part.split('-')
                    result.extend(list(range(int(start), int(stop)+1)))
                else:
                    result.append(int(part))
            return result
        else:
            return datum if hasattr(datum, '__iter__') else [ datum ]
        
    @staticmethod
    def _as_range(iterable): 
        l = list(iterable)
        if len(l) > 1:
            return '{0}-{1}'.format(l[0], l[-1])
        else:
            return '{0}'.format(l[0])

    def __repr__(self):        
        return 'intspan({0!r})'.format(self.__str__())
    
    def __str__(self):
        items = sorted(self)
        return ','.join(self._as_range(g) for _, g in groupby(items, key=lambda n, c=count(): n-next(c)))

    # see Jeff Mercado's answer to http://codereview.stackexchange.com/questions/5196/grouping-consecutive-numbers-into-ranges-in-python-3-2
    # see also: http://stackoverflow.com/questions/2927213/python-finding-n-consecutive-numbers-in-a-list


# It might be interesting to have a metaclass factory that could create
# spansets of things other than integers. For example, enumerateds defined
# by giving a universe of possible options. Or characters.
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.