Skip to content

Commit 418cc5c

Browse files
committed
Added crush(), a recursive flatten().
1 parent 3c58ed0 commit 418cc5c

1 file changed

Lines changed: 53 additions & 4 deletions

File tree

src/bd2k/util/iterables.py

Lines changed: 53 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ def flatten( iterables ):
4545
return chain.from_iterable( iterables )
4646

4747

48+
# noinspection PyPep8Naming
4849
class concat( object ):
4950
"""
5051
A literal iterable that lets you combine sequence literals (lists, set) with generators or list
@@ -66,6 +67,9 @@ class concat( object ):
6667
>>> list( concat( 1, xrange( 2, 4 ), 4 ) )
6768
[1, 2, 3, 4]
6869
70+
It only does so one level deep. If you need to recursively flatten a data structure,
71+
check out crush().
72+
6973
If you want to prevent that flattening for an iterable argument, wrap it in concat():
7074
7175
>>> list( concat( 1, concat( xrange( 2, 4 ) ), 4 ) )
@@ -75,13 +79,13 @@ class concat( object ):
7579
7680
>>> list( concat() ) # empty concat
7781
[]
78-
>>> list( concat( 1 ) ) # non-iterable singleton
82+
>>> list( concat( 1 ) ) # non-iterable
7983
[1]
80-
>>> list( concat( concat() ) ) # empty, iterable singleton
84+
>>> list( concat( concat() ) ) # empty iterable
8185
[]
82-
>>> list( concat( concat( 1 ) ) ) # singleton singleton
86+
>>> list( concat( concat( 1 ) ) ) # singleton iterable
8387
[1]
84-
>>> list( concat( 1, concat( 2 ), 3 ) )
88+
>>> list( concat( 1, concat( 2 ), 3 ) ) # flattened iterable
8589
[1, 2, 3]
8690
>>> list( concat( 1, [2], 3 ) ) # flattened iterable
8791
[1, 2, 3]
@@ -118,3 +122,48 @@ def expand( x ):
118122
return i
119123

120124
return flatten( imap( expand, self.args ) )
125+
126+
127+
# noinspection PyPep8Naming
128+
class crush( object ):
129+
"""
130+
>>> list(crush([]))
131+
[]
132+
>>> list(crush([[]]))
133+
[]
134+
>>> list(crush([1]))
135+
[1]
136+
>>> list(crush([[1]]))
137+
[1]
138+
>>> list(crush([[[]]]))
139+
[]
140+
>>> list(crush([1,(),['two'],([3, 4],),{5}]))
141+
[1, 'two', 3, 4, 5]
142+
143+
>>> list(crush(1))
144+
Traceback (most recent call last):
145+
...
146+
TypeError: 'int' object is not iterable
147+
148+
>>> list(crush('123'))
149+
['1', '2', '3']
150+
151+
The above is a bit of an anomaly since strings occurring inside iterables are not broken up:
152+
153+
>>> list(crush(['123']))
154+
['123']
155+
"""
156+
157+
def __init__( self, iterables ):
158+
super( crush, self ).__init__( )
159+
self.iterables = iterables
160+
161+
def __iter__( self ):
162+
def expand( x ):
163+
try:
164+
# Using __iter__() instead of iter() prevents breaking up of strings
165+
return crush( x.__iter__( ) )
166+
except AttributeError:
167+
return x,
168+
169+
return flatten( imap( expand, self.iterables ) )

0 commit comments

Comments
 (0)