Reputation: 1863
I'm looking for a one liner to convert
[[1], [1, 1], [1, 1, 1], [1], [1], [1], [1, 1, 1, 1]]
to
[[1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1, 1]]
where the algorithm combines the lists up to a certain threshold length.
I currently have this
batched = []
batch = []
for l in lists:
batch.extend(l)
if len(batch) > threshold:
batched.append(batch)
batch = []
Upvotes: 2
Views: 231
Reputation: 1863
I appreciate all the help, and I learned that a one liner is not be best for this problem as it is ugly and unreadable and probably inefficient. That being said I did come up with this which seems to work from a different slightly cleaner approach.
>>> from functools import reduce
>>> l = [[1], [2, 3], [4, 5, 6], [7], [8], [9], [10, 11, 12, 13]]
>>> t = 3
>>> b = reduce(lambda p, n: p[:-1] + [p[-1] + n] if len(p[-1]) + len(n) <= t or not p[-1] else p + [n], l, [[]])
>>> b
[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12, 13]]
Upvotes: 0
Reputation: 17263
There's nothing wrong with your original implementation but if you insist oneliner here's one ugly option:
from itertools import accumulate, chain, groupby
THRESHOLD = 3
l = [[1], [1, 1], [1, 1, 1], [1], [1], [1], [1, 1, 1, 1]]
res = [[y for x in g for y in x[1]]
for k, g in groupby(zip(chain([0], accumulate(len(x) for x in l)), l),
lambda x: x[0] // THRESHOLD)]
print(res)
Output:
[[1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1, 1]]
The idea is to generate list of (item count so far, sublist)
tuples and group them by dividing count by THRESHOLD
.
>>> temp = list(zip(chain([0], accumulate(len(x) for x in l)), l))
>>> temp
[(0, [1]), (1, [1, 1]), (3, [1, 1, 1]), (6, [1]), (7, [1]), (8, [1]), (9, [1, 1, 1, 1])]
>>> groups = [list(g) for k, g in groupby(temp, lambda x: x[0] // THRESHOLD)]
>>> groups
[[(0, [1]), (1, [1, 1])], [(3, [1, 1, 1])], [(6, [1]), (7, [1]), (8, [1])], [(9, [1, 1, 1, 1])]]
>>> [[y for x in g for y in x[1]] for g in groups]
[[1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1, 1]]
Upvotes: 5
Reputation: 29690
This may not be ideal, but it would be my attempt. The idea is to chain your lists together with itertools.chain
, then pull from the chain with itertools.islice
and append a new list until you cannot any longer. It admittedly isn't a one liner.
from itertools import chain, islice
def grouper(n, li):
it = chain(*li)
out_l = []
while True:
chunk = list(islice(it, n))
if len(chunk) < n:
if chunk:
out_l[-1] += chunk
return out_l
out_l.append(chunk)
Demo
In[238]: orig = [[1], [1, 1], [1, 1, 1], [1], [1], [1], [1, 1, 1, 1]]
In[239]: grouper(3, orig)
Out[239]: [[1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1, 1]]
In[240]: grouper(4, orig)
Out[240]: [[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1, 1]]
In[241]: grouper(5, orig)
Out[241]: [[1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1]]
Out[242]: grouper(1, orig)
Out[242]: [[1], [1], [1], [1], [1], [1], [1], [1], [1], [1], [1], [1], [1]]
Upvotes: 3
Reputation: 13175
Cracked it but only because I'm stubborn. It's really ugly and inefficient though and maybe there's a cleaner way but, even if there is, it's not worth it.
a = [[1], [1, 1], [1, 1, 1], [2], [1], [1], [1, 1, 1, 1]]
b = [[[item for sublist in a for item in sublist][x+(y*3)] for x in range(1,4)] for y in range(0, (len([i for j in a for i in j])/3))]
EDIT: Because I was testing this in Python 2.7 I missed the fact that division works differently in Python 3. Thanks @nexus66 for pointing out a modification (which just makes it even longer!).
c = [[[item for sublist in a for item in sublist][x+(y*3)] for x in range(1,4)] for y in range(0, int((len([i for j in a for i in j])/3)))]
Upvotes: 4
Reputation: 12515
This is an ugly one-liner... It's not exactly what you asked for (pretty close), but maybe it'll give you some idea of how to approach it...
arr = [[1], [1, 1], [1, 1, 1], [1], [1], [1], [1, 1, 1, 1]]
threshold = 4
[[x for y in arr for x in y][i:i+threshold]
for i, _ in enumerate([x for y in arr for x in y])
if i % threshold == 0]
Out[31]:
[[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1], [1]]
Upvotes: 0