简体   繁体   中英

Python - How to sort multidimensional list to two-dimensional list?

How i can sort multidimensional list to two-dimensional list?

Multidimensional input: [8, [6, 7, [-1], [4, [[10]]], 2], 1]

Desired two-dimensional output: [[8, 1], [6, 7, 2], [-1, 4], [], [10]]

all same depth list items need to be in same list.

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 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.

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]]


To show How it works , I split the method in two:

 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.

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]]

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]]

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]]

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM