Search code examples
pythoncombinationspython-itertools

all possible combinations of dicts based on values inside dicts


I want to generate all possible ways of using dicts, based on the values in them. To explain in code, I have:

a = {'name' : 'a', 'items': 3}
b = {'name' : 'b', 'items': 4}
c = {'name' : 'c', 'items': 5}

I want to be able to pick (say) exactly 7 items from these dicts, and all the possible ways I could do it in.

So:

x = itertools.product(range(a['items']), range(b['items']), range(c['items']))
y = itertools.ifilter(lambda i: sum(i)==7, x)

would give me:

(0, 3, 4)
(1, 2, 4)
(1, 3, 3)
...

What I'd really like is:

({'name' : 'a', 'picked': 0}, {'name': 'b', 'picked': 3}, {'name': 'c', 'picked': 4})
({'name' : 'a', 'picked': 1}, {'name': 'b', 'picked': 2}, {'name': 'c', 'picked': 4})
({'name' : 'a', 'picked': 1}, {'name': 'b', 'picked': 3}, {'name': 'c', 'picked': 3})
....

Any ideas on how to do this, cleanly?


Solution

  • Here it is

    import itertools
    import operator
    
    a = {'name' : 'a', 'items': 3}
    b = {'name' : 'b', 'items': 4}
    c = {'name' : 'c', 'items': 5}
    
    dcts = [a,b,c]
    
    x = itertools.product(range(a['items']), range(b['items']), range(c['items']))
    y = itertools.ifilter(lambda i: sum(i)==7, x)
    z = (tuple([[dct, operator.setitem(dct, 'picked', vval)][0] \
           for dct,vval in zip(dcts, val)]) for val in y)
    for zz in z:
        print zz
    

    You can modify it to create copies of dictionaries. If you need a new dict instance on every iteration, you can change z line to

    z = (tuple([[dct, operator.setitem(dct, 'picked', vval)][0] \
          for dct,vval in zip(map(dict,dcts), val)]) for val in y)