Tarun K Tarun K - 3 months ago 6
Python Question

Split a list of dictionary if the value is empty?

I want to split a list of dictionary

if the value is empty
and create new list of list.

Input :

[{'k':'a'},{'k':'b'},{'k':''},{'k':'d'},{'k':''},{'k':'f'},{'k':'g'}]


Output :

[[{'k': 'a'}, {'k': 'b'}, {'k': ''}], [{'k': 'd'}, {'k': ''}], [{'k': 'f'}, {'k': 'g'}]]


I have tried using loop, if and its working fine.

sub_header_list = [{'k':'a'},{'k':'b'},{'k':''},{'k':'d'},{'k':''},{'k':'f'},{'k':'g'}]
index_data = [] ; data_list = []
for i in sub_header_list:
index_data.append(i)
if i['k'] == '':
data_list.append(index_data)
index_data = []
print(data_list+[index_data])

[[{'k': 'a'}, {'k': 'b'}, {'k': ''}], [{'k': 'd'}, {'k': ''}], [{'k': 'f'}, {'k': 'g'}]]


Is there any pythonic way to achieve the same, i mean by using in-built functions or something else ?

Answer

You can use a groupby:

from itertools import groupby, chain


l = [{'k':'a'},{'k':'b'},{'k':''},{'k':'d'},{'k':''},{'k':'f'},{'k':'g'}]

grps = groupby(l, lambda d: d["k"] == "")

print([list(chain(*(v, next(grps, [[], []])[1]))) for k, v in grps if k])

Output:

[[{'k': 'a'}, {'k': 'b'}, {'k': ''}], [{'k': 'd'}, {'k': ''}], [{'k': 'f'}, {'k': 'g'}]]

Or use a generator function:

def grp(lst, ):
    temp = []
    for dct in lst:
        # would catch None, 0, for just empty strings use if dct["k"] == "".
        if not dct["k"]: 
            temp.append(dct)
            yield temp
            temp = []
        else:
            temp.append(dct)
    yield temp

Which gives you the same output:

In [9]: list(grp(l))
Out[9]: 
[[{'k': 'a'}, {'k': 'b'}, {'k': ''}],
 [{'k': 'd'}, {'k': ''}],
 [{'k': 'f'}, {'k': 'g'}]]

The generator function is by far the most efficient approach.

In [8]: l = [{'k':'a'}, {'k':'b'}, {'k':''}, {'k':'d'}, {'k':''}, {'k':'f'}, {'k':'g'}]

In [9]: l = [dict(choice(l)) for _ in range(100000)]

In [10]: timeit list(grp(l))
10 loops, best of 3: 19.5 ms per loop

In [11]: %%timeit
index_list = [i + 1 for i, x in enumerate(l) if x == {'k': ''}]
[l[i:j] for i, j in zip([0] + index_list, index_list + [len(l)])]
   ....: 
10 loops, best of 3: 31.6 ms per loop

In [12]: %%timeit                 grps = groupby(l, lambda d: d["k"] == "")
[list(chain(*(v, next(grps, [[], []])[1]))) for k, v in grps if k]
   ....: 
10 loops, best of 3: 40 ms per loop