Operation on every pair of element in a list
Using Python, I'd like to compare every possible pair in a list.
Suppose I have
my_list = [1,2,3,4]
I'd like to do an operation (let's call it foo) on every combination of 2 elements from the list.
The final result should be the same as
foo(1,1)
foo(1,2)
...
foo(4,3)
foo(4,4)
My first thought was to iterate twice through the list manually, but that doesn't seem very pythonic.
Check out product()
in the itertools
module. It does exactly what you describe.
import itertools
my_list = [1,2,3,4]
for pair in itertools.product(my_list, repeat=2):
foo(*pair)
This is equivalent to:
my_list = [1,2,3,4]
for x in my_list:
for y in my_list:
foo(x, y)
Edit: There are two very similar functions as well, permutations()
and combinations()
. To illustrate how they differ:
product()
generates every possible pairing of elements, including all duplicates:
1,1 1,2 1,3 1,4
2,1 2,2 2,3 2,4
3,1 3,2 3,3 3,4
4,1 4,2 4,3 4,4
permutations()
generates all unique orderings of each unique pair of elements, eliminating the x,x
duplicates:
. 1,2 1,3 1,4
2,1 . 2,3 2,4
3,1 3,2 . 3,4
4,1 4,2 4,3 .
Finally, combinations()
only generates each unique pair of elements, in lexicographic order:
. 1,2 1,3 1,4
. . 2,3 2,4
. . . 3,4
. . . .
All three of these functions were introduced in Python 2.6.
I had a similar problem and found the solution here. It works without having to import any module.
Supposing a list like:
people = ["Lisa","Pam","Phil","John"]
A simplified one-line solution would look like this.
All possible pairs, including duplicates:
result = [foo(p1, p2) for p1 in people for p2 in people]
All possible pairs, excluding duplicates:
result = [foo(p1, p2) for p1 in people for p2 in people if p1 != p2]
Unique pairs, where order is irrelevant:
result = [foo(people[p1], people[p2]) for p1 in range(len(people)) for p2 in range(p1+1,len(people))]
In case you don't want to operate but just to get the pairs, removing the function foo
and using just a tuple would be enough.
All possible pairs, including duplicates:
list_of_pairs = [(p1, p2) for p1 in people for p2 in people]
Result:
('Lisa', 'Lisa')
('Lisa', 'Pam')
('Lisa', 'Phil')
('Lisa', 'John')
('Pam', 'Lisa')
('Pam', 'Pam')
('Pam', 'Phil')
('Pam', 'John')
('Phil', 'Lisa')
('Phil', 'Pam')
('Phil', 'Phil')
('Phil', 'John')
('John', 'Lisa')
('John', 'Pam')
('John', 'Phil')
('John', 'John')
All possible pairs, excluding duplicates:
list_of_pairs = [(p1, p2) for p1 in people for p2 in people if p1 != p2]
Result:
('Lisa', 'Pam')
('Lisa', 'Phil')
('Lisa', 'John')
('Pam', 'Lisa')
('Pam', 'Phil')
('Pam', 'John')
('Phil', 'Lisa')
('Phil', 'Pam')
('Phil', 'John')
('John', 'Lisa')
('John', 'Pam')
('John', 'Phil')
Unique pairs, where order is irrelevant:
list_of_pairs = [(people[p1], people[p2]) for p1 in range(len(people)) for p2 in range(p1+1,len(people))]
Result:
('Lisa', 'Pam')
('Lisa', 'Phil')
('Lisa', 'John')
('Pam', 'Phil')
('Pam', 'John')
('Phil', 'John')
Edit: After the rework to simplify this solution, I realised it is the same approach than Adam Rosenfield. I hope the larger explanation helps some to understand it better.
If you're just calling a function, you can't really do much better than:
for i in my_list:
for j in my_list:
foo(i, j)
If you want to collect a list of the results of calling the function, you can do:
[foo(i, j) for i in my_list for j in my_list]
which will return you a list of the result of applying foo(i, j)
to each possible pair (i, j)
.