The Python Coding Stack: The Networking Event (#4 in The itertools Series • `combinations()` and `pe
"I'll get the one-to-one meetings sorted", Yteria offered. She was at the one and only meeting of the organising committee for the local Python group. There were five of them sitting around a coffee shop table meant for two people, laptops jostling for table space, balanced precariously at the edge of the table. They'd been a bit disorganised in getting everything sorted, and there was a bit of panic now.
But Yteria couldn't offer to do more. She had picked up quite a bit of work recently, and she had to prioritise her paid gigs. She'll handle setting up the rotas for the one-to-one speed-dating-type networking meetings.
Meeting over. On her way home, she took a slight detour to walk past her local word shop. It had been many months sincethat unfortunate day when she lost the word "for". Yteria's world is a bit different to yours and mine. Words can be lost or stolen. And when you no longer have a word, you can't speak it, think it, write it, or type it.
She had already enquired at the word shop how much it costs to buy "for". It wasn't cheap. But she had learned to adapt to speaking without using "for". She had also adapted her Python coding style well!
Creating All Possible Pairings Using NestedforLoops
The list of participants wasn't finalised yet, but Yteria knew she'd have a list of names to work with on the morning of the event. She just needed code that could accept a list of names.
In the networking hour, every participant should meet every other participant for a short one-to-one chat. Yteria's job was to create the rotas. For now, she can just use a list with the organisers' names:
Why don't you have a go at writing a few lines of code to create all possible one-to-one pairings between the people in this list?
Ready?
Here's one possible solution:
This is probably the most straightforward solution. You iterate through the list of participants. Then, for each of the people in the list, you iterate again through the same list of participants. You check you haven't picked the same name twice since there's no point in a person having a one-to-one meeting with themselves, right? And you also check that the pairing is not already included, since Mikey-Yteria and Yteria-Mikey refer to the same one-to-one meeting.
Note the two sets of parentheses when you append the names to the listpairs, since you append a tuple topairsin each iteration.
Let's print out the pairings:
The output shows all possible pairings:
Pairs (using nested `for` loops): Mikey - Yteria Mikey - Sam Mikey - Alex Mikey - Jordan Yteria - Sam Yteria - Alex Yteria - Jordan Sam - Alex Sam - Jordan Alex - Jordan
Or, if you prefer a different solution, you could try the following:
This time, you useenumerate()in the outer loop to also access the indices as you iterate throughparticipants. Then, you only need to loop through part of the list in the inner loop by using the sliceindex + 1:. The colon at the end is a shortcut for taking the slice fromindex + 1to the end of the list. You can read more about slicing inA Slicing Story. This version gives the same pairings as the previous code.
The Version Without aforLoop
But Yteria couldn't use either of those versions. Until she finds a way of acquiring the word "for" again, she can't writeforloops.
And theitertoolsmodule has come to the rescue many times for Yteria. This module does what it says on the tin–it provides tools useful for iteration. If you missed other posts in this series, you can find them all here:The `itertools` Series.
Yteria's task was to find all the combinations of two-person groupings. And for those of you familiar with maths terminology, you'll know thatcombinationsis a thing in discrete mathematics. And theitertoolsmodule also has acombinations()function.
Here's how Yteria used it:
You pass two arguments toitertools.combinations():
The list namedparticipantswith all the people's names. You can use anyiterableas an argument.
The integer2, which represents the size of the groupings you need.
Yteria was looking for groups of two people–pairs–which is why the second argument is2.
As you've learnt earlier inThe `itertools` Series, the functions initertoolsreturniterators. Let's explore this code in an interactive REPL:
The variable namepairsrefers to the iterator returned bycombinations(). That's why you don't see the pairings displayed. But you can cast the iterator to a list:
You can experiment with different integers as the second argument incombinations()to create groupings of different sizes.
Let's get back to Yteria's script. She still needs to display the groupings, but she still can't useforloops. Here's a rather clumsy solution to print out the pairings without using aforloop–but unless you have Yteria's unique problem, don't do this!
Here's the output from this code, confirming you get the same pairings as with the earlier code:
Pairs (using `itertools.combinations()`): Mikey - Yteria Mikey - Sam Mikey - Alex Mikey - Jordan Yteria - Sam Yteria - Alex Yteria - Jordan Sam - Alex Sam - Jordan Alex - Jordan
This series is aboutitertools, and the clumsy code Yteria used to print out the pairings doesn't use any of theitertoolsfunctions. Still,map()is another tool that can be used for iteration. In the code above, the items in the iteratorpairsare mapped ontothe `lambda` functionthat returns anf-string. Themap()function also returns an iterator. So, you use the unpacking operator*when you usemap()as an argument inprint(). This is another iteration tool!
Cool. But Is This Solution Better?
Bettercan be a subjective term. We can debate which is easier to read. Theforloop versions are well-understood by most Python programmers, whatever their level of expertise. Theitertoolsversion is readable only if you know aboutitertools.combinations(). But once you know about this function and what it does, theitertoolsversion becomes more readable. You can understand what the code does instantly without having to work your way through the logic of the nestedforloops.
How about efficiency? Theitertoolsversion ensures that you only create the list, which uses up memory, when you need it, and not earlier, since theitertoolsfunctions return iterators. You could achieve the same effect withforloops, but it requires a bit more effort.
How about time efficiency? Let's compare the three versions and see how long Python takes to execute these solutions:
You enclose the code for each algorithm in triple-quoted strings, which you then use as arguments intimeit.timeit(). Here's the output from this code–the usual reminder that the timings will be different on your computer:
Using for loops v1: 0.20314587499888148 Using for loops v2: 0.0652464590020827 Using itertools: 0.03185112500068499
The nestedforloops option, which iterates through each list twice in full, takes the longest. The secondforloop version is more efficient since the code doesn't need to go through each element again in the inner loop.
However, the version usingitertools.combinations()is about twice as fast as the faster of the twoforloop versions. You can try other options, too, such as using list comprehensions, if you prefer. However, theitertoolsfunctions are likely to be quicker than most other solutions since they're optimised for the iteration tasks they're designed for.
But What If The Order of The Pairings Matters?
But what if the order of the groupings matters? What if it matters who's first and who's second? Go and fetch your discrete mathematics textbook again. Or don't bother. Here's what you need. Instead of all thecombinationsof two people from the list, now you need all thepermutations.Anditertoolshas apermutations()function:
Each pairing is listed twice in the output since order matters:
Mikey - Yteria Mikey - Sam Mikey - Alex Mikey - Jordan Yteria - Mikey Yteria - Sam Yteria - Alex Yteria - Jordan Sam - Mikey Sam - Yteria Sam - Alex Sam - Jordan Alex - Mikey Alex - Yteria Alex - Sam Alex - Jordan Jordan - Mikey Jordan - Yteria Jordan - Sam Jordan - Alex
And One More Tool
There's another function to complete the set of combinatorial tools initertools. Try usingitertools.combinations_with_replacement(instead ofitertools.combinations(). Do you spot the difference?
Final Words
And there's another task Yteria managed to complete without needing to write aforloop. Theitertoolsmodule came to the rescue, again, with some help frommap()and the unpacking operator*.
The networking event turned out OK, too…
Support The Python Coding Stack
Code in this article uses Python 3.13
The code images used in this article are created usingSnappify.[Affiliate link]
You can also support this publication by making aone-off contribution of any amount you wish.
Support The Python Coding Stack
For more Python resources, you can also visitReal Python—you may even stumble on one of my own articles or courses there!
Also, are you interested in technical writing? You’d like to make your own writing more narrative, more engaging, more memorable? Have a look atBreaking the Rules.
And you can find out more about me atstephengruppetta.com
Further reading related to this article’s topic:
Loss of Words • A Nested Journey (# 1 in TheitertoolsSeries •product())
Iterable: Python's Stepping Stones
A One-Way Stream of Data • Iterators in Python (Data Structure Categories #6)
What's All the Fuss About `lambda` Functions in Python
The Curious Little Shop at The End of My Street • Python's f-strings
Appendix: Code Blocks
Code Block #1
participants = ["Mikey", "Yteria", "Sam", "Alex", "Jordan"]
Code Block #2
participants = ["Mikey", "Yteria", "Sam", "Alex", "Jordan"] pairs = [] for first in participants: for second in participants: if first != second and (second, first) not in pairs: pairs.append((first, second))
Code Block #3
# ... print("Pairs (using nested `for` loops):") for pair in pairs: print(f"{pair[0]:6} - {pair[1]}")
Code Block #4
pairs = [] for index, first in enumerate(participants): for second in participants[index + 1:]: pairs.append((first, second))
Code Block #5
import itertools participants = ["Mikey", "Yteria", "Sam", "Alex", "Jordan"] pairs = itertools.combinations(participants, 2)
Code Block #6
import itertools participants = ["Mikey", "Yteria", "Sam", "Alex", "Jordan"] pairs = itertools.combinations(participants, 2) pairs # <itertools.combinations object at 0x1039b5490>
Code Block #7
# ... list(pairs) # [('Mikey', 'Yteria'), ('Mikey', 'Sam'), ('Mikey', 'Alex'), ('Mikey', 'Jordan'), # ('Yteria', 'Sam'), ('Yteria', 'Alex'), ('Yteria', 'Jordan'), ('Sam', 'Alex'), # ('Sam', 'Jordan'), ('Alex', 'Jordan')]
Code Block #8
import itertools participants = ["Mikey", "Yteria", "Sam", "Alex", "Jordan"] pairs = itertools.combinations(participants, 2) print("Pairs (using `itertools.combinations()`):") print( *map(lambda pair: f"{pair[0]:6} - {pair[1]}", pairs), sep="\n", )
Code Block #9
import itertools import timeit participants = ["Mikey", "Yteria", "Sam", "Alex", "Jordan"] using_for_loops_v1 = """ pairs = [] for first in participants: for second in participants: if first != second and (second, first) not in pairs: pairs.append((first, second)) """ using_for_loops_v2 = """ pairs = [] for index, first in enumerate(participants): for second in participants[index + 1:]: pairs.append((first, second)) """ using_itertools = "list(itertools.combinations(participants, 2))" print( "Using for loops v1:", timeit.timeit(using_for_loops_v1, globals=globals(), number=100_000), ) print( "Using for loops v2:", timeit.timeit(using_for_loops_v2, globals=globals(), number=100_000), ) print( "Using itertools:", timeit.timeit(using_itertools, globals=globals(), number=100_000), )
Code Block #10
import itertools participants = ["Mikey", "Yteria", "Sam", "Alex", "Jordan"] pairs = itertools.permutations(participants, 2) print("Pairs (using `itertools.permutations()`):") print( *map(lambda pair: f"{pair[0]:6} - {pair[1]}", pairs), sep="\n", )
For more Python resources, you can also visitReal Python—you may even stumble on one of my own articles or courses there!
Also, are you interested in technical writing? You’d like to make your own writing more narrative, more engaging, more memorable? Have a look atBreaking the Rules.
And you can find out more about me atstephengruppetta.com