Change nested list into multiple single lists

Viewed 224

If I have a list:

[[ A, B, C ], 10, [ 1, 2, 3 ], F]

What would be the best way to change it into:

[ A, 10, 1, F]
[ B, 10, 2, F]
[ C, 10, 3, F]

The length of the nested lists are consistent: len(list[0]) == len(list[2]), but len(list[0]) might be 3, 2, 4, etc.

6 Answers

Let's try itertools here:

from itertools import cycle

list(zip(*(iter(x) if isinstance(x, list) else cycle([x]) for x in l)))
# [('A', 10, 1, 'F'), ('B', 10, 2, 'F'), ('C', 10, 3, 'F')]

Note that this will not error out if the sub-lists are unequally sized - the size of the output is equal to the size of the shortest sub-list.


How It Works

Iterate over the list, if the item is a list then convert it into an iterator with iter, otherwise if scalar convert it into a circular iterator (infinitely repeating single value) and zip them together and listify.

Step 0

 A     10    1      F
 B           2   
 C           3
l[0]  l[1]  l[2]  l[3]

Step 1

(iter(x) if isinstance(x, list) else cycle([x]) for x in l)

 A     10    1      F
 B     10    2      F
 C     10    3      F
l[0]  l[1]  l[2]  l[3]

Step 2

list(zip(*_))
[('A', 10, 1, 'F'), ('B', 10, 2, 'F'), ('C', 10, 3, 'F')]
l = [[ 'A', 'B', 'C' ], 10, [ 1, 2, 3 ], 'F']
mxl = len(max(l, key= lambda x: len(x) if type(x) is list else 1)) # length of our output
ret = [[] for i in range(mxl)] # creating a list of mxl emply lists
for i in l: # going through input
    if type(i) is list: # if input is list going through list
        for j,v in enumerate(i):
            ret[j].append(v)
    else: # if input is not list adding it mxl times
        for j in range(mxl): ret[j].append(i)
for i in ret: print(i) # printing output
['A', 10, 1, 'F']
['B', 10, 2, 'F']
['C', 10, 3, 'F']

If you only use single value and lists with the same lenght you can try that

l1 = [[ 'A', 'B', 'C' ], 10, [ 1, 2, 3 ], 'F']

def func(list_, len_):
    list2 = [[] for x in range(len_)]
    for x in list_:
        if type(x) == list:
            for count2, y in enumerate(x):
                list2[count2].append(y)
        else:
            for y in range(len_):
                list2[y].append(x)
    return list2

print('\n'.join([str(x) for x in func(l1, 3)]))

There is probably a better way to do it.

list1=[[ 'A', 'B', 'C' ], 10, [ 1, 2, 3 ], 'F']
max_len=max([len(x) for x in list1 if type(x)==list])
type_list=[type(x) for x in list1]
major=[]
for i in range(max_len):
    minor=[]
    for index,datatype in enumerate(type_list):
        if datatype==list:
            minor.append(list1[index][i])
        else:
            minor.append(list1[index])
    major.append(minor)

enter image description here

Explanation: calculate 2 things 1st, max length of an element present in list & 2nd a list storing datatype of each element. Now, looping over max length found, using an inner loop, iterate over the datatype list created earlier & figure out between list/non_list and append accordingly

Here is a way using list subscriptions:

l = [[ 'A', 'B', 'C' ], 10, [ 1, 2, 3 ], 'F']

l = [[l[0][i],l[1],l[2][i],l[3]] for i in range(len(l)-1)]

for a in l:
    print(a)

Output:

['A', 10, 1, 'F']
['B', 10, 2, 'F']
['C', 10, 3, 'F']

You can also do it by unpacking a zipped list

mylist = [[ 'A', 'B', 'C' ], [10], [ 1, 2, 3 ], ['F']]

for i in mylist:
    for n in i:
        while len(i)< max([len(i) for i in mylist]):
            i.append(n)

unziplist = [list(i) for i in zip(*mylist)]
print(unziplist)

Outputs

[['A', 10, 1, 'F'], ['B', 10, 2, 'F'], ['C', 10, 3, 'F']]
Related