Skip to content

Commit

Permalink
Feed the hobgoblins (delint).
Browse files Browse the repository at this point in the history
  • Loading branch information
jaraco committed Nov 14, 2017
1 parent 0701b5f commit f3f8ecf
Showing 1 changed file with 64 additions and 21 deletions.
85 changes: 64 additions & 21 deletions jaraco/itertools.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ def make_rows(num_columns, seq):
# of rows
return zip(*result)


def bisect(seq, func=bool):
"""
Split a sequence into two sequences: the first is elements that
Expand All @@ -66,6 +67,7 @@ def bisect(seq, func=bool):
queues = GroupbySaved(seq, func)
return queues.get_first_n_queues(2)


class GroupbySaved(object):
"""
Split a sequence into n sequences where n is determined by the
Expand Down Expand Up @@ -99,7 +101,8 @@ class GroupbySaved(object):
>>> next(ones)
4
"""
def __init__(self, sequence, func = lambda x: x):

def __init__(self, sequence, func=lambda x: x):
self.sequence = iter(sequence)
self.func = func
self.queues = collections.OrderedDict()
Expand All @@ -120,7 +123,7 @@ def __fetch__(self):
def __find_queue__(self, key):
"search for the queue indexed by key"
try:
while not key in self.queues:
while key not in self.queues:
self.__fetch__()
return self.queues[key]
except StopIteration:
Expand All @@ -142,6 +145,7 @@ def get_first_n_queues(self, n):
values.extend(iter([]) for n in range(missing))
return values


class FetchingQueue(queue.Queue):
"""
A FIFO Queue that is supplied with a function to inject more into
Expand All @@ -157,6 +161,7 @@ class FetchingQueue(queue.Queue):
tuple(q) just copies the elements in the list (of which there are
none).
"""

def __init__(self, fetcher):
if six.PY3:
super(FetchingQueue, self).__init__()
Expand All @@ -177,6 +182,7 @@ def __iter__(self):
def enqueue(self, item):
self.put_nowait(item)


class Count(object):
"""
A stop object that will count how many times it's been called and return
Expand Down Expand Up @@ -208,6 +214,7 @@ class Count(object):
If all you need is the count of items, consider :class:`Counter` instead.
"""

def __init__(self, limit):
self.count = 0
self.limit = limit if limit is not None else float('Inf')
Expand Down Expand Up @@ -245,6 +252,7 @@ class islice(object):
>>> print(islice(3, 10, 2))
every 2nd item from 3 to 9
"""

def __init__(self, *sliceArgs):
self.sliceArgs = sliceArgs

Expand All @@ -259,7 +267,10 @@ def __str__(self):
return result

def _formatArgs(self):
slice_range = lambda a_b: '%d to %d' % (a_b[0], a_b[1] - 1)

def slice_range(a_b):
return '%d to %d' % (a_b[0], a_b[1] - 1)

if len(self.sliceArgs) == 1:
result = 'at most %d' % self.sliceArgs
if len(self.sliceArgs) == 2:
Expand All @@ -270,6 +281,7 @@ def _formatArgs(self):
result = 'every %(ord)s item from %(range)s' % locals()
return result


class LessThanNBlanks(object):
"""
An object that when called will return True until n false elements
Expand All @@ -278,14 +290,16 @@ class LessThanNBlanks(object):
Can be used with filter or itertools.ifilter, for example:
>>> import itertools
>>> sampleData = ['string 1', 'string 2', '', 'string 3', '', 'string 4', '', '', 'string 5']
>>> sampleData = ['string 1', 'string 2', '', 'string 3', '',
... 'string 4', '', '', 'string 5']
>>> first = itertools.takewhile(LessThanNBlanks(2), sampleData)
>>> tuple(first)
('string 1', 'string 2', '', 'string 3')
>>> first = itertools.takewhile(LessThanNBlanks(3), sampleData)
>>> tuple(first)
('string 1', 'string 2', '', 'string 3', '', 'string 4')
"""

def __init__(self, nBlanks):
self.limit = nBlanks
self.count = 0
Expand All @@ -296,6 +310,7 @@ def __call__(self, arg):
raise ValueError("Should not call this object anymore.")
return self.count < self.limit


class LessThanNConsecutiveBlanks(object):
"""
An object that when called will return True until n consecutive
Expand All @@ -304,7 +319,8 @@ class LessThanNConsecutiveBlanks(object):
Can be used with filter or itertools.ifilter, for example:
>>> import itertools
>>> sampleData = ['string 1', 'string 2', '', 'string 3', '', 'string 4', '', '', 'string 5']
>>> sampleData = ['string 1', 'string 2', '', 'string 3', '', 'string 4',
... '', '', 'string 5']
>>> first = itertools.takewhile(LessThanNConsecutiveBlanks(2), sampleData)
>>> tuple(first)
('string 1', 'string 2', '', 'string 3', '', 'string 4', '')
Expand All @@ -324,6 +340,7 @@ def __call__(self, arg):
raise ValueError("Should not call this object anymore.")
return self.count < self.limit


class splitter(object):
"""
object that will split a string with the given arguments for each call.
Expand All @@ -332,7 +349,8 @@ class splitter(object):
>>> list(s('hello, world, this is your, master calling'))
['hello', ' world', ' this is your', ' master calling']
"""
def __init__(self, sep = None):

def __init__(self, sep=None):
self.sep = sep

def __call__(self, s):
Expand All @@ -346,6 +364,7 @@ def __call__(self, s):
yield s[lastIndex:]
break


def grouper_nofill_str(n, iterable):
"""
Take a sequence and break it up into chunks of the specified size.
Expand Down Expand Up @@ -401,6 +420,7 @@ class Counter(object):
>>> items.count
20
"""

def __init__(self, i):
self.count = 0
self._orig_iter = iter(i)
Expand All @@ -419,6 +439,8 @@ def GetCount(self):
return self.count

# todo, factor out caching capability


class iterable_test(dict):
"""
Test objects for iterability, caching the result by type
Expand All @@ -429,7 +451,8 @@ class iterable_test(dict):
>>> test[[]]
True
"""
def __init__(self, ignore_classes=six.string_types+(six.binary_type,)):

def __init__(self, ignore_classes=six.string_types + (six.binary_type,)):
"""ignore_classes must include str, because if a string
is iterable, so is a single character, and the routine runs
into an infinite recursion"""
Expand All @@ -451,6 +474,7 @@ def _test(self, candidate):
self[type(candidate)] = result
return result


def iflatten(subject, test=None):
if test is None:
test = iterable_test()
Expand All @@ -461,6 +485,7 @@ def iflatten(subject, test=None):
for subelem in iflatten(elem, test):
yield subelem


def flatten(subject, test=None):
"""
Flatten an iterable with possible nested iterables.
Expand All @@ -483,12 +508,14 @@ def flatten(subject, test=None):
"""
return list(iflatten(subject, test))


def empty():
"""
An empty iterator.
"""
return iter(tuple())


def is_empty(iterable):
"""
Return whether the iterable is empty or not. Consumes at most one item
Expand All @@ -505,6 +532,7 @@ def is_empty(iterable):
return True
return False


class Reusable(object):
"""
An iterator that may be reset and reused.
Expand All @@ -527,7 +555,8 @@ def __init__(self, iterable):
self.__saved = iterable
self.reset()

def __iter__(self): return self
def __iter__(self):
return self

def reset(self):
"""
Expand All @@ -547,6 +576,7 @@ def __next__(self):
raise
next = __next__


def every_other(iterable):
"""
Yield every other item from the iterable
Expand All @@ -559,6 +589,7 @@ def every_other(iterable):
yield next(items)
next(items)


def remove_duplicates(iterable, key=None):
"""
Given an iterable with items that may come in as sequential duplicates,
Expand All @@ -577,6 +608,7 @@ def remove_duplicates(iterable, key=None):
itertools.groupby(iterable, key)
)))


def skip_first(iterable):
"""
Skip the first element of an iterable
Expand All @@ -586,6 +618,7 @@ def skip_first(iterable):
"""
return itertools.islice(iterable, 1, None)


def peek(iterable):
"""
Get the next value from an iterable, but also return an iterable
Expand All @@ -602,6 +635,7 @@ def peek(iterable):
peeker, original = itertools.tee(iterable)
return next(peeker), original


class Peekable(object):
"""
Wrapper for a traditional iterable to give it a peek attribute.
Expand Down Expand Up @@ -688,6 +722,7 @@ def first(iterable):
iterable = iter(iterable)
return next(iterable)


def last(iterable):
"""
Return the last item from the iterable, discarding the rest.
Expand All @@ -706,6 +741,7 @@ def last(iterable):
except NameError:
raise ValueError("Iterable contains no items")


def one(item):
"""
Return the first element from the iterable, but raise an exception
Expand Down Expand Up @@ -735,6 +771,7 @@ def one(item):
result, = item
return result


def nwise(iter, n):
"""
Like pairwise, except returns n-tuples of adjacent items.
Expand All @@ -746,6 +783,7 @@ def nwise(iter, n):
next(iterset[-1], None)
return six.moves.zip(*iterset)


def window(iter, pre_size=1, post_size=1):
"""
Given an iterable, return a new iterable which yields triples of
Expand Down Expand Up @@ -773,6 +811,7 @@ def window(iter, pre_size=1, post_size=1):
next(post_iter, None)
return six.moves.zip(pre_iter, iter, post_iter)


class IterSaver(object):
def __init__(self, n, iterable):
self.n = n
Expand All @@ -785,6 +824,7 @@ def __next__(self):
return self.buffer.popleft()
next = __next__


def partition_items(count, bin_size):
"""
Given the total number of items, determine the number of items that
Expand All @@ -808,6 +848,7 @@ def partition_items(count, bin_size):
bins[i % num_bins] += 1
return bins


def balanced_rows(n, iterable, fillvalue=None):
"""
Like grouper, but balance the rows to minimize fill per row.
Expand All @@ -821,6 +862,7 @@ def balanced_rows(n, iterable, fillvalue=None):
row = itertools.chain(row, [fillvalue])
yield tuple(row)


def reverse_lists(lists):
"""
>>> reverse_lists([[1,2,3], [4,5,6]])
Expand Down Expand Up @@ -884,6 +926,7 @@ def suppress_exceptions(callables, *exceptions):
except exceptions:
pass


def apply(func, iterable):
"""
Like 'map', invoking func on each item in the iterable,
Expand All @@ -905,16 +948,16 @@ def apply(func, iterable):


def list_or_single(iterable):
"""
Given an iterable, return the items as a list. If the iterable contains
exactly one item, return that item. Correlary function to always_iterable.
>>> list_or_single(iter('abcd'))
['a', 'b', 'c', 'd']
>>> list_or_single(['a'])
'a'
"""
result = list(iterable)
if len(result) == 1:
result = result[0]
return result
"""
Given an iterable, return the items as a list. If the iterable contains
exactly one item, return that item. Correlary function to always_iterable.
>>> list_or_single(iter('abcd'))
['a', 'b', 'c', 'd']
>>> list_or_single(['a'])
'a'
"""
result = list(iterable)
if len(result) == 1:
result = result[0]
return result

0 comments on commit f3f8ecf

Please sign in to comment.