How i can sort multidimensional list to two-dimensional list?
Multidimensional input: [8, [6, 7, [-1], [4, [[10]]], 2], 1]
Desired two-dimension
You could perhaps use a defaultdict here to measure the depth of each element, along with recursion:
from collections import defaultdict
L = [8, [6, 7, [-1], [4, [[10]]], 2], 1]
res = defaultdict(list)
def myfunc(L, depth):
for i in L:
if isinstance(i, list):
myfunc(i, depth+1)
else:
res[depth].append(i)
myfunc(L, 0)
The defaultdict will then look like this:
defaultdict(<class 'list'>, {0: [8, 1], 1: [6, 7, 2], 2: [-1, 4], 4: [10]})
You'll then need to translate the defaultdict back to what you want. Note that the default dict will not contain an empty list because it can't detect it (ie: [[10]]
and [10]
are both lists), but what it will have is a gap in the range (notice how the depth 3
is missing in the defaultdict).
final = []
for i in range(max(res)+1):
if i not in res:
final.append([])
else:
final.append(res[i])
print(final)
Very messy, I'm sure improvements could be made.
The recursive approach taken by the other answers comes with the recursion limit imposed by Python and the overhead of two passes. A more efficient one-pass iterative approach is to implement breadth-first search using a queue of tuples of lists and associated depths:
from collections import deque
def flatten(lst):
output = []
q = deque([(lst, 0)])
while q:
l, depth = q.popleft()
for i in l:
if isinstance(i, list):
q.append((i, depth + 1))
else:
while depth >= len(output):
output.append([])
output[-1].append(i)
return output
so that:
flatten([8, [6, 7, [-1], [4, [[10]]], 2], 1])
returns:
[[8, 1], [6, 7, 2], [-1, 4], [], [10]]
The idea is basically the same that the one in @TerryA answer, but using setdefault and checking at the end of the for loop if something of the depth was added:
lst = [8, [6, 7, [-1], [4, [[10]]], 2], 1]
def depths(l):
def flatten(l, start=0, depth={}):
for e in l:
if isinstance(e, list):
flatten(e, start=start + 1, depth=depth)
else:
depth.setdefault(start, []).append(e)
if start not in depth:
depth[start] = []
d = {}
flatten(l, depth=d)
return [d[i] for i in range(max(d) + 1)]
result = depths(lst)
print(result)
Output
[[8, 1], [6, 7, 2], [-1, 4], [], [10]]
You can do this by first generating a dictionary of elements at each depth (with depth as key in this dictionary and list of elements of that depth as value). The recursive function get_elements_by_depth
below does this. Then all you need to do is flatten the values of that dictionary. (the function flatten_by_depth
below does what you need).
from collections import defaultdict
def get_elements_by_depth(ls, cur_depth, cur_dict):
"""
returns a dictionary with depth as key and a list of all
elements that have that depth as value
"""
for x in ls:
if isinstance(x, list):
get_elements_by_depth(x, cur_depth + 1, cur_dict)
else:
cur_dict[cur_depth].append(x)
return cur_dict
def flatten_by_depth(ls):
"""
returns a list of lists, where the list at index i
contains all elements of depth i
"""
elements_by_depth = get_elements_by_depth(ls, 0, defaultdict(list))
max_depth = max(elements_by_depth.keys())
# Since we're using a defaultdict, we don't have to worry about
# missing keys in elements_by_depth
return [
elements_by_depth[i]
for i in xrange(max_depth + 1)
]
> flatten_by_depth([8, [6, 7, [-1], [4, [[10]]], 2], 1])
[[8, 1], [6, 7, 2], [-1, 4], [], [10]]
Somebody recently posted a similar question which, by the time I composed my answer, was closed as a duplicate. So I figured I'd add my answer here.
def extract_entries(nested_list, new_list=[]):
# Add the list of all of the items in <nested_list> that are NOT lists themselves.
new_list.append( [ e for e in nested_list if type(e) != list ] )
# Look at entries in <nested_list> that ARE lists themselves.
for entry in nested_list:
if type(entry) == list:
extract_entries(entry, new_list)
return new_list
Testing:
M = [8, [6, 7, [-1], [4, [[10]]], 2], 1]
print(extract_entries(M))
# Returns: [[8, 1], [6, 7, 2], [-1], [4], [], [10]]
My option with recursion and without any dependencies:
lst = [8, [6, 7, [-1], [4, [[10]]], 2], 1]
def flat_group(lst, deep = 0, res = None):
if res == None: res = []
for item in lst:
if len(res) <= deep: res.append([])
if not type(item) == list:
res[deep].append((item))
else:
flat_group(item, deep + 1, res)
return res
print(flat_group(lst))
#=> [[8, 1], [6, 7, 2], [-1, 4], [], [10]]
def flat(lst, deep = 0, res = []):
for item in lst:
if not type(item) == list:
res.append((deep, item))
else:
flat(item, deep + 1, res)
return res
def group(lst):
flatten = flat(lst)
max_n = max(flatten)[0]
res = [[] for _ in range(0,max_n+1)]
for deep, item in flatten:
res[deep].append(item)
return res
print(group(lst))
#=> [[8, 1], [6, 7, 2], [-1, 4], [], [10]]
flat(lst)
is a recursive method that builds a flat list of tuples where each tuple contains the value and the deep inside the original list.
So the call flat(lst)
returns:
# [(0, 8), (1, 6), (1, 7), (2, -1), (2, 4), (4, 10), (1, 2), (0, 1)]
Then group(lst)
builds a list of n+1
empty sub-list, where n
is the maximum depth, it iterates over the result of flat(lst)
and append each element by index to the proper sub-list.
The flat_group(lst)
does almost the same.