-
Notifications
You must be signed in to change notification settings - Fork 0
/
itertoolsrecipes.py
168 lines (125 loc) · 4.21 KB
/
itertoolsrecipes.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
#!/usr/bin/python
'''From:
https://docs.python.org/3.8/library/itertools.html#itertools-recipes
Using the names as used in Haskell code as these seem to be the cleanest.
'''
from operator import eq as equal
from itertools import islice, chain, count
def take(n, iterable):
"Return first n items of the iterable as a list"
return list(islice(iterable, n))
class DifferentLengthsError(IndexError):
pass
def first_n(generator, n=None):
'''Developed from: http://wiki.python.org/moin/Generators
http://linuxgazette.net/100/pramode.html
'''
if n is not None:
result = take(n, generator)
if len(result) < n:
raise DifferentLengthsError(
'Requested amount exceeds the number in the iteration.')
return result
else:
return generator
def skip_first_n(generator, n=None):
return islice(generator, n, None)
def nth_term(generator, n=None):
if (n is not None) and (n < 0):
raise ValueError('Index must be above zero; %r is invalid.' % (n))
for item in first_n(generator, n):
value = item
# "value" should have a value otherwise "FirstN" would have
# raised a "DifferentLengthsError".
return value
def nth_term_zero_based(generator, n=None):
if n is not None:
if n <= 0:
raise ValueError(
'Index must be zero or above; %r is invalid.' % (n))
else:
return nth_term(generator, n+1)
else:
assert n is None
return nth_term(generator, n)
def zip_error_if_different_lengths(*collections):
iterators = tuple(map(iter, collections))
while True:
ended = None
output = []
for iterator in iterators:
try:
output.append(next(iterator))
except StopIteration:
new_ended = True
else:
new_ended = False
if (ended is not None) and (ended != new_ended):
raise DifferentLengthsError(
'The iterations are of unequal lengths.')
ended = new_ended
if ended:
break
yield tuple(output)
def items_equal(*items):
if items:
first = items[0]
others = items[1:]
return all(equal(first, other) for other in others)
else:
return True
def collections_equal(*collections):
if collections:
try:
return all(
items_equal(*items)
for items in zip_error_if_different_lengths(*collections))
except DifferentLengthsError:
return False
else:
return True
def len_of_generator(gen):
'''Will use up the generator, use itertools.tee to make independently
usable copies beforehand if required.
Found at:
http://stackoverflow.com/questions/393053/length-of-generator-output
'''
return sum(1 for _ in gen)
def len_of_generator_is_not_less_than(gen, minimum_len):
'''The idea being to save resources by allowing generators to terminate earlier
when the length is known to be enough for some purpose.
'''
min_len = 0
if minimum_len < min_len:
raise ValueError(
'Length must be at least %r, %r rejected.' % (min_len, minimum_len))
if minimum_len == min_len:
return True
for current_len, item in enumerate(gen, 1):
if minimum_len <= current_len:
return True
else:
return False
# testing an idea for showing the surrounding padding
def rolling_collection(items, sample_size, pad=0, pad_value=None):
if sample_size < 1:
raise ValueError('Sample Size must be at least 1.')
if pad < 0:
raise ValueError('Padding should be at least 0.')
padding_items = (pad_value,) * pad
items = chain(padding_items, items, padding_items)
items = tuple(items)
for i in range(len(items)-sample_size+1):
yield items[i:i+sample_size]
def are_consecutive(items):
items = iter(items)
try:
first_value = next(items)
except StopIteration:
return True
else:
return all(
item == expected
for item, expected in zip(
chain([first_value], items),
count(first_value)))