Skip to content

Instantly share code, notes, and snippets.

@ggirelli
Last active February 8, 2020 12:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ggirelli/932e0479268aa81d71f4c735fa91c44a to your computer and use it in GitHub Desktop.
Save ggirelli/932e0479268aa81d71f4c735fa91c44a to your computer and use it in GitHub Desktop.
Python3.6+ class to convert a printer-like page sequence into an Iterator
'''
@author: Gabriele Girelli
@contact: gigi.ga90@gmail.com
'''
import re
from typing import Iterator, List, Optional, Pattern, Tuple
class MultiRange(object):
__current_item: Tuple[int] = (0, 0)
__string_range: Optional[str] = None
__extremes_list: Optional[List[Tuple[int]]] = None
__reg: Pattern = re.compile(r'^[0-9-, ]+$')
__length: Optional[int] = None
__ready: bool = False
def __init__(self, s: str):
super(MultiRange, self).__init__()
assert self.__reg.search(s) is not None, (
"cannot parse range string. It should only contains numbers, "
+ "commas, dashes, and spaces.")
self.__string_range = s
string_range_list = [b.strip() for b in self.__string_range.split(",")]
self.__extremes_list = []
for string_range in string_range_list:
extremes = [int(x) for x in string_range.split("-")]
if 1 == len(extremes):
extremes = [extremes[0], extremes[0]]
assert 2 == len(extremes), "a range should be specified as A-B"
assert extremes[1] >= extremes[0]
self.__extremes_list.append(tuple(extremes))
self.__extremes_list = sorted(
self.__extremes_list, key=lambda x: x[0])
self.__clean_extremes_list()
assert 0 < self.__extremes_list[0][0], "'page' count starts from 1."
self.__ready = True
@property
def length(self):
if self.__length is None and self.__ready:
self.__length = 0
for a, b in self.__extremes_list:
self.__length += b-a+1
return self.__length
def __clean_extremes_list(self) -> None:
is_clean = False
while not is_clean:
popped = 0
i = 0
while i < len(self.__extremes_list)-1:
A = self.__extremes_list[i]
B = self.__extremes_list[i+1]
if A[1] >= B[0] and A[1] < B[1]:
self.__extremes_list[i] = (A[0], B[1])
self.__extremes_list.pop(i+1)
popped = 1
break
elif A[1] >= B[1]:
self.__extremes_list.pop(i+1)
popped = 1
break
i += 1
if i >= len(self.__extremes_list)-2+popped:
is_clean = True
def __next__(self) -> int:
current_range_id, current_item = self.__current_item
if current_range_id >= len(self.__extremes_list):
raise StopIteration
current_range = self.__extremes_list[current_range_id]
if current_item >= current_range[1]-current_range[0]:
self.__current_item = (current_range_id+1, 0)
else:
self.__current_item = (current_range_id, current_item+1)
return current_range[0]+current_item
def __iter__(self) -> Iterator[int]:
self.__current_item = (0, 0)
return self
def __len__(self) -> Optional[int]:
return self.length
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment