I have the following dict:
groups = {"group 1": [1, 2, 3, 4],
"group 2": [5, 6, 7, 8],
"group 3": [9, 10, 11, 12],
"group 4": [13, 14]}
When the length of a group is smaller than a minimum size (group_size=4
), I want to redistribute the members to the other groups. The result in this case would be something like:
groups = {"group 1": [1, 2, 3, 4, 13],
"group 2": [5, 6, 7, 8, 14],
"group 3": [9, 10, 11, 12]}
I have the following code, which works, but is less efficient than I'd like:
# Identify small groups
small_groups = []
for group_name, group_members in groups.items():
if len(group_members) < group_size:
small_groups.append(group_name)
# Redistribute members of small groups to the larger groups
to_redistribute = []
for group_name in small_groups:
to_redistribute.extend(groups.pop(group_name))
for group_name, group_members in groups.items():
if not to_redistribute:
break
group_members.append(to_redistribute.pop())
Important note: The real members of groups are strings, not integers.
Is there a better way to redistribute dictionary value lists?
Your solution is good, but you can combine the pop and re-distribution logic together using itertools.cycle
.
from itertools import cycle
for k in list(groups.keys()):
if len(groups[k]) < group_size:
for v, k_ in zip(groups.pop(k), cycle(groups.keys())):
groups[k_].append(v)
The idea is to keep cycling through keys to re-distribute the data equally. It determines—at each iteration—whether a group is over the threshold or not. If a group is valid, then augmenting it later (via re-distribution) will never bring it under the threshold. However groups that are initially under the threshold (but are not reached until later during future iterations) can possibly become valid if and when you augment values to it from another group that was removed. If that does not happen, then it will be removed and its data redistributed on a future iteration.
Keep in mind, groups that were initially slated for removal may now become valid after redistribution, so our solutions will differ in the output for some inputs.
print(groups)
{'group 1': [1, 2, 3, 4, 13],
'group 2': [5, 6, 7, 8, 14],
'group 3': [9, 10, 11, 12]}
filter
and sum
to pull out concatenated lists with lengths less than 4
4
from itertools import cycle
f = lambda v: len(v) < 4
x = sum(filter(f, groups.values()), [])
g = {k: v for k, v in groups.items() if not f(v)}
c = cycle(g)
while x:
g[next(c)].append(x.pop())
g
{'group 1': [1, 2, 3, 4, 14],
'group 2': [5, 6, 7, 8, 13],
'group 3': [9, 10, 11, 12]}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With