Python convert different depth list of tuples to flatten list of tuples
Question:
I have a tuple of characters like such:
p= [((4.0, 4.0), '->', ((4, 2), (4, 8)), ((2, 2), (5, 5))), ((4.0, 7.0), '->', ((4, 2), (4, 8)), ((5, 6), (3, 8)))]`
How do I convert it to a tupple so that it is like:
p = [(4.0,4.0), (4, 2), (4, 8), (2, 2), (5, 5),(4.0, 7.0), (4, 2), (4, 8), (5, 6), (3, 8) ]
`
I was trying this way but output is coming with double bracket for some
res = []
for tup in p:
for sub_tup in tup:
print sub_tup, type(sub_tup)
if type(sub_tup) == tuple:
res.append(sub_tup)
print(res)
Output
[(4.0, 4.0), ((4, 2), (4, 8)), ((2, 2), (5, 5)), (4.0, 7.0), ((4, 2), (4, 8)), ((5, 6), (3, 8))]
Answers:
This solution uses an external library more_itertools
.
Here’s a rundown:
- Collapse the
p
structure all the way down to non-iterable elements. The result is a 1-D array/list.
- Remove
'->'
from the list.
- Take every two elements from the list and put them together.
from pprint import pprint
from more_itertools import collapse, sliced
p = [((4.0, 4.0), '->', ((4, 2), (4, 8)), ((2, 2), (5, 5))), ((4.0, 7.0), '->', ((4, 2), (4, 8)), ((5, 6), (3, 8)))]
p2 = collapse(p)
p3 = filter(lambda x: isinstance(x, (int, float)), p2)
p4 = sliced(list(p3), 2)
pprint(list(p4))
Result:
[[4.0, 4.0],
[4, 2],
[4, 8],
[2, 2],
[5, 5],
[4.0, 7.0],
[4, 2],
[4, 8],
[5, 6],
[3, 8]]
Use list comprehension to flatten list and then isinstance
to filter out strings.
p= [((4.0, 4.0), '->', ((4, 2), (4, 8)), ((2, 2), (5, 5))), ((4.0, 7.0), '->', ((4, 2), (4, 8)), ((5, 6), (3, 8)))]
output = [m if not isinstance(m, (int, float)) else n for o in p for n in o for m in n if not isinstance(n, str)]
output = [item for i, item in enumerate(output) if output[i] != output[i-1]]
First output
almost produces the right output but with a duplicate of the item preceding the ‘->’, so we just remove the duplicates in the next line.
You can flatten the list recursively manually. This is not the most elegant way to do that, but it returns the right result:
L = []
def flatten(p):
tupFlag = 0
for el in p:
if type(el) == tuple:
tupFlag = 1
e = flatten(el)
if e != None:
L.append(flatten(e))
if tupFlag==0:
return p
flatten(p)
print(L)
Here’s a recursive solution, traversing iterables until a 2-tuple of numbers is found, and ignoring anything non-iterable.
from pprint import pprint
p = [((4.0, 4.0), '->', ((4, 2), (4, 8)), ((2, 2), (5, 5))), ((4.0, 7.0), '->', ((4, 2), (4, 8)), ((5, 6), (3, 8)))]
def get_pairs(item):
pairs = []
if isinstance(item, (list, tuple)):
# if item is a pair of numbers
if (len(item) == 2 and
isinstance(item[0], (int, float)) and
isinstance(item[1], (int, float))):
return [item]
else:
# item is a non-basic iterable
for subitem in item:
pairs.extend(get_pairs(subitem))
else:
# item is not list or tuple, ignore it
pass
return pairs
x = get_pairs(p)
pprint(x)
Result:
[(4.0, 4.0),
(4, 2),
(4, 8),
(2, 2),
(5, 5),
(4.0, 7.0),
(4, 2),
(4, 8),
(5, 6),
(3, 8)]
You can write a recursive solution like the below:
p = [((4.0, 4.0), '->', ((4, 2), (4, 8)), ((2, 2), (5, 5))), ((4.0, 7.0), '->', ((4, 2), (4, 8)), ((5, 6), (3, 8)))]
def rec_flat(tpl, result):
for t in tpl:
if type(t[0]) not in [str, tuple]:
result.append(t)
elif isinstance(t, tuple):
rec_flat(t, result)
result = []
rec_flat(p, result)
print(result)
Output:
[(4.0, 4.0), (4, 2), (4, 8), (2, 2), (5, 5), (4.0, 7.0), (4, 2), (4, 8), (5, 6), (3, 8)]
I have a tuple of characters like such:
p= [((4.0, 4.0), '->', ((4, 2), (4, 8)), ((2, 2), (5, 5))), ((4.0, 7.0), '->', ((4, 2), (4, 8)), ((5, 6), (3, 8)))]`
How do I convert it to a tupple so that it is like:
p = [(4.0,4.0), (4, 2), (4, 8), (2, 2), (5, 5),(4.0, 7.0), (4, 2), (4, 8), (5, 6), (3, 8) ]
`
I was trying this way but output is coming with double bracket for some
res = []
for tup in p:
for sub_tup in tup:
print sub_tup, type(sub_tup)
if type(sub_tup) == tuple:
res.append(sub_tup)
print(res)
Output
[(4.0, 4.0), ((4, 2), (4, 8)), ((2, 2), (5, 5)), (4.0, 7.0), ((4, 2), (4, 8)), ((5, 6), (3, 8))]
This solution uses an external library more_itertools
.
Here’s a rundown:
- Collapse the
p
structure all the way down to non-iterable elements. The result is a 1-D array/list. - Remove
'->'
from the list. - Take every two elements from the list and put them together.
from pprint import pprint
from more_itertools import collapse, sliced
p = [((4.0, 4.0), '->', ((4, 2), (4, 8)), ((2, 2), (5, 5))), ((4.0, 7.0), '->', ((4, 2), (4, 8)), ((5, 6), (3, 8)))]
p2 = collapse(p)
p3 = filter(lambda x: isinstance(x, (int, float)), p2)
p4 = sliced(list(p3), 2)
pprint(list(p4))
Result:
[[4.0, 4.0],
[4, 2],
[4, 8],
[2, 2],
[5, 5],
[4.0, 7.0],
[4, 2],
[4, 8],
[5, 6],
[3, 8]]
Use list comprehension to flatten list and then isinstance
to filter out strings.
p= [((4.0, 4.0), '->', ((4, 2), (4, 8)), ((2, 2), (5, 5))), ((4.0, 7.0), '->', ((4, 2), (4, 8)), ((5, 6), (3, 8)))]
output = [m if not isinstance(m, (int, float)) else n for o in p for n in o for m in n if not isinstance(n, str)]
output = [item for i, item in enumerate(output) if output[i] != output[i-1]]
First output
almost produces the right output but with a duplicate of the item preceding the ‘->’, so we just remove the duplicates in the next line.
You can flatten the list recursively manually. This is not the most elegant way to do that, but it returns the right result:
L = []
def flatten(p):
tupFlag = 0
for el in p:
if type(el) == tuple:
tupFlag = 1
e = flatten(el)
if e != None:
L.append(flatten(e))
if tupFlag==0:
return p
flatten(p)
print(L)
Here’s a recursive solution, traversing iterables until a 2-tuple of numbers is found, and ignoring anything non-iterable.
from pprint import pprint
p = [((4.0, 4.0), '->', ((4, 2), (4, 8)), ((2, 2), (5, 5))), ((4.0, 7.0), '->', ((4, 2), (4, 8)), ((5, 6), (3, 8)))]
def get_pairs(item):
pairs = []
if isinstance(item, (list, tuple)):
# if item is a pair of numbers
if (len(item) == 2 and
isinstance(item[0], (int, float)) and
isinstance(item[1], (int, float))):
return [item]
else:
# item is a non-basic iterable
for subitem in item:
pairs.extend(get_pairs(subitem))
else:
# item is not list or tuple, ignore it
pass
return pairs
x = get_pairs(p)
pprint(x)
Result:
[(4.0, 4.0),
(4, 2),
(4, 8),
(2, 2),
(5, 5),
(4.0, 7.0),
(4, 2),
(4, 8),
(5, 6),
(3, 8)]
You can write a recursive solution like the below:
p = [((4.0, 4.0), '->', ((4, 2), (4, 8)), ((2, 2), (5, 5))), ((4.0, 7.0), '->', ((4, 2), (4, 8)), ((5, 6), (3, 8)))]
def rec_flat(tpl, result):
for t in tpl:
if type(t[0]) not in [str, tuple]:
result.append(t)
elif isinstance(t, tuple):
rec_flat(t, result)
result = []
rec_flat(p, result)
print(result)
Output:
[(4.0, 4.0), (4, 2), (4, 8), (2, 2), (5, 5), (4.0, 7.0), (4, 2), (4, 8), (5, 6), (3, 8)]