443,730 Members | 1,659 Online
Need help? Post your question and get tips & solutions from a community of 443,730 IT Pros & Developers. It's quick & easy.

# Creating unique combinations from lists

 P: n/a I have three lists... for instance a = ['big', 'small', 'medium']; b = ['old', 'new']; c = ['blue', 'green']; I want to take those and end up with all of the combinations they create like the following lists ['big', 'old', 'blue'] ['small', 'old', 'blue'] ['medium', 'old', 'blue'] ['big', 'old', 'green'] ['small', 'old', 'green'] ['medium', 'small', 'green'] ['big', 'new', 'blue'] ['small', 'new', 'blue'] ['medium', 'new', 'blue'] ['big', 'new', 'green'] ['small', 'new', 'green'] ['medium', 'new', 'green' ] I could do nested for ... in loops, but was looking for a Pythonic way to do this. Ideas? Jan 16 '08 #1
11 Replies

 P: n/a -----Original Message----- From: py********************************@python.org [mailto:python- li*************************@python.org] On Behalf Of breal Sent: Wednesday, January 16, 2008 2:15 PM To: py*********@python.org Subject: Creating unique combinations from lists I have three lists... for instance a = ['big', 'small', 'medium']; b = ['old', 'new']; c = ['blue', 'green']; I want to take those and end up with all of the combinations they create like the following lists ['big', 'old', 'blue'] ['small', 'old', 'blue'] ['medium', 'old', 'blue'] ['big', 'old', 'green'] ['small', 'old', 'green'] ['medium', 'small', 'green'] ['big', 'new', 'blue'] ['small', 'new', 'blue'] ['medium', 'new', 'blue'] ['big', 'new', 'green'] ['small', 'new', 'green'] ['medium', 'new', 'green' ] I could do nested for ... in loops, but was looking for a Pythonic way to do this. Ideas? http://www.python.org/dev/peps/pep-0202/ Jan 16 '08 #2

 P: n/a On Jan 16, 11:33 am, "Reedick, Andrew"

 P: n/a I could do nested for ... in loops, but was looking for a Pythonic way to do this. Ideas? I find nested for loops very Pythonic. Explicit is better than implicit, and simple is better than complex. Regards, Martin Jan 16 '08 #4

 P: n/a a = ['big', 'small', 'medium']; b = ['old', 'new']; c = ['blue', 'green']; I want to take those and end up with all of the combinations they create like the following lists ['big', 'old', 'blue'] ['small', 'old', 'blue'] ['medium', 'old', 'blue'] ['big', 'old', 'green'] ['small', 'old', 'green'] ['medium', 'small', 'green'] ['big', 'new', 'blue'] ['small', 'new', 'blue'] ['medium', 'new', 'blue'] ['big', 'new', 'green'] ['small', 'new', 'green'] ['medium', 'new', 'green' ] I could do nested for ... in loops, but was looking for a Pythonic way to do this. Ideas? You can use a recursive generator: def iterall(*iterables): if iterables: for head in iterables[0]: for remainder in iterall(*iterables[1:]): yield [head] + remainder else: yield [] for thing in iterall( ['big', 'medium', 'small'], ['old', 'new'], ['blue', 'green'], ): print thing The two for-loops plus recursion should handle any number of parameters, so if you were so inclined, you could do for thing in iterall( ['big', 'medium', 'small'], ['old', 'new'], ['blue', 'green'], ['smelly', 'fragrant'], ['spatula', 'avocado'], ): print thing and get all 3*2*2*2*2 items. Or count in binary: for i, bitstream in enumerate(iterall( [0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1], )): print ''.join(map(str, bitstream)), '=', i When you're iterating over combinations of items in groups of lists, I prefer the clarity of this over something like [(a,b,c,d,e) for a in [0,1] for b in [0,1] for c in [0,1] for d in [0,1] for e in [0,1]] -tkc Jan 16 '08 #5

 P: n/a On Jan 16, 11:15 am, breal

 P: n/a On Wed, 16 Jan 2008 11:15:16 -0800, breal wrote: I could do nested for ... in loops, but was looking for a Pythonic way to do this. Ideas? What makes you think nested loops aren't Pythonic? -- Steven Jan 16 '08 #7

 P: n/a >I could do nested for ... in loops, but was looking for a Pythonic way >to do this. Ideas? What makes you think nested loops aren't Pythonic? On their own, nested loops aren't a bad thing. I suspect they become un-Pythonic when they make code look ugly and show a broken model of the problem. There's a big diffence between: # iterate over a 10x10 grid for i in xrange(10): for j in xrange(10): print i,j which is pretty manageable, but quickly becomes very unpythonic if the problem is poorly defined: for a in range(5): for b in range(5): for c in range(5): for d in range(5): for e in range(5): for f in range(5): for g in range(5): for h in range(5): for i in range(5): for j in range(5): for k in range(5): for l in range(5): for m in range(5): for n in range(5): for o in range(5): for p in range(5): for q in range(5): for r in range(5): for s in range(5): for t in range(5): for u in range(5): for v in range(5): for w in range(5): for x in range(5): for y in range(5): for z in range(5): print a,b,c,d,e,f,g, print h,i,j,k,l,m,n, print o,p,q,r,s,t,u, print v,w,x,y,z It gets even worse if your loop nesting is based on something external. You wouldn't want code that looks like if len(input) == 2: for a in range(5): for b in range(5): whatever(a,b) elif len(input) == 3: for a in range(5): for b in range(5): for c in range(5): whatever(a,b,c) elif len(input) == 4: ... Contributing to the unpythonic'ness (unpythonicity?) of it is that something is clearly happening at a higher level than just for-loops so other Python constructs should be used to express them instead of abusing your code to do your dirty work. -tkc Jan 16 '08 #8

 P: n/a > for a in range(5): .... for z in range(5): means the inner loop runs 5**26 times so perhaps it's not only unpythonic but also uncomputable... Jan 16 '08 #9

 P: n/a > for a in range(5): ... > for z in range(5): means the inner loop runs 5**26 times so perhaps it's not only unpythonic but also uncomputable... only if you're impatient ;) yes, it was a contrived pessimal example. It could be range(2) to generate boolean-number sequences. I've done 2**26 loops in code before (well, it was on the way to 2**32, just to see how long it took to roll over and hit an error condition). The main emphasis was to show that there was a pattern unfolding that should have been translated into more pythonic code than just hard-coding nested loops. -tkc Jan 16 '08 #10

 P: n/a The main emphasis was to show that there was a pattern unfolding that should have been translated into more pythonic code than just hard-coding nested loops. Practicality beats purity. That you would solve a more general problem in a more general way doesn't mean that you shouldn't solve the more specific problem (combinations from three sets) in a specific, easy-to-read way. Readability counts. I find your solution (with nested generators) *very* unpythonic. It is much more complicated than necessary to solve the problem at hand, and it doesn't get Pythonic just by using the latest language features. It may be a smart solution, but not a Pythonic one. Regards, Martin P.S. To solve the general problem, I like http://aspn.activestate.com/ASPN/Coo.../Recipe/496807 Jan 17 '08 #11

 P: n/a On Thu, 17 Jan 2008 10:44:51 -0600, Reedick, Andrew wrote: >-----Original Message-----From: Tim Chase [mailto:py*********@tim.thechases.com] Sent: Thursday,January 17, 2008 10:30 AM To: Reedick, AndrewCc: breal; py*********@python.org; ma****@v.loewis.de Subject: Re:Creating unique combinations from listsYick...a nice demo of the power of eval, but definitely filed under the"Hack" heading You hurt my feeling. *sniffle* Given how late python compiles/evaluates code blocks, I'm thinking that eval() is less hack and more paradigm ..err.. pythonic. ;-) I see your smiley, but even so, do you have any idea how many times eval is used in the standard library? Not very often. \$ pwd /usr/lib/python2.5 \$ grep -r "eval(.*)" *.py | wc -l 20 Some of those twenty matches are false positives. I manually inspected them, and by my count there are just ten actual uses of eval: bdb.py: return eval(expr, globals, locals) dumbdbm.py: key, pos_and_siz_pair = eval(line) gettext.py: return eval('lambda n: int(%s)' % plural) gopherlib.py: _type_to_name_map[eval(name)] = name[2:] mhlib.py: def do(s): print s; print eval(s) os.py: eval(name) pdb.py: x = eval(arg, {}, {}) rexec.py: return eval(code, m.__dict__) rlcompleter.py: object = eval(expr, self.namespace) warnings.py: cat = eval(category) I haven't made any effort to determine how many of them are gaping great big security holes. -- Steven Jan 18 '08 #12

### This discussion thread is closed

Replies have been disabled for this discussion.