102

Say I have an array/list of things I want to compare. In languages I am more familiar with, I would do something like

for (int i = 0, i < mylist.size(); i++)
    for (int j = i + 1, j < mylist.size(); j++)
        compare(mylist[i], mylist[j])

This ensures we only compare each pair once. For some context, I am doing collision detection on a bunch of objects contained in the list. For each collision detected, a small 'collision' object describing the collision is appended to a list, which another routine then loops through resolving each collision (depending on the nature of the two colliding objects). Obviously, I only want to report each collision once.

Now, what is the pythonic way of doing this, since Python favors using iterators rather than looping over indices?

I had the following (buggy) code:

for this in mylist:
    for that in mylist:
        compare(this, that)

But this clearly picks up each collision twice, which lead to some strange behavior when trying to resolve them. So what is the pythonic solution here?

poke
  • 369,085
  • 72
  • 557
  • 602
Bogdanovist
  • 1,498
  • 2
  • 11
  • 20
  • 1
    I am not sure what "compare" means. What I would do is tu put one list in a hash table and then will do a loop over the second list. At each iteration I will make a lookup for the comparison. – Luixv May 17 '13 at 07:06

5 Answers5

188

Of course this will generate each pair twice as each for loop will go through every item of the list.

You could use some itertools magic here to generate all possible combinations:

import itertools
for a, b in itertools.combinations(mylist, 2):
    compare(a, b)

itertools.combinations will pair each element with each other element in the iterable, but only once.


You could still write this using index-based item access, equivalent to what you are used to, using nested for loops:

for i in range(len(mylist)):
    for j in range(i + 1, len(mylist)):
        compare(mylist[i], mylist[j])

Of course this may not look as nice and pythonic but sometimes this is still the easiest and most comprehensible solution, so you should not shy away from solving problems like that.

poke
  • 369,085
  • 72
  • 557
  • 602
  • 1
    Great! Thanks for the explanation and links. I wasn't aware of the itertools module, but it looks handy. – Bogdanovist May 17 '13 at 11:39
  • 1
    @poke `name 'compare' is not defined` how can I remedy this? – 3kstc Feb 28 '18 at 22:50
  • 2
    @3kstc I took that from the question. Replace that by whatever you want to do with each pair of items. – poke Feb 28 '18 at 22:52
  • 3
    If you want index, just add `index=range(len(mylist))` and `for a, b in itertools.combinations(index, 2):` then `compare(mylist[a], mylist[b])`. Now you can use the index to get the element from `mylist`. – allenyllee Dec 19 '18 at 11:02
  • Your outer loop is still longer than needed. You don't need to compare the last element with anything else because it's been already compared with all the others in the previous iterations. So you can make it `for i in range(len(mylist) - 1):` instead. – Diogo Eichert Apr 27 '21 at 14:28
  • 1
    @DiogoEichert As I wrote in my answer, the for loop example is a direct _equivalent_ to OP’s code. It was never my goal to replicate a smarter approach there. You should use itertools in that case since it skips all duplicate matches. – poke Apr 27 '21 at 15:15
  • @poke And I just told you how you can improve it by adding just 4 more characters. You don't even need an extra import for that. – Diogo Eichert Apr 28 '21 at 17:29
37

Use itertools.combinations(mylist, 2)

mylist = range(5)
for x,y in itertools.combinations(mylist, 2):
    print x,y

0 1
0 2
0 3
0 4
1 2
1 3
1 4
2 3
2 4
3 4
shx2
  • 61,779
  • 13
  • 130
  • 153
14

I think using enumerate on the outer loop and using the index to slice the list on the inner loop is pretty Pythonic:

for index, this in enumerate(mylist):
    for that in mylist[index+1:]:
        compare(this, that)
alan
  • 3,246
  • 1
  • 32
  • 36
  • At first I thought that the "index+1" might result in index out of bounds error when we reach the final index of the outer for loop, but in Python list slicing if you give a start position that is greater than or equal to the length of a list then it simple gives and empty list – Ghos3t Jun 03 '20 at 02:05
  • 1
    @Ghos3t A convenient feature for sure. If you want to skip the last iteration since it's not necessary you would just slice the last element off the list being enumerated: `mylist[:-1]`. – alan Jun 03 '20 at 15:37
0

Find the other answer for this program in simple way..

def Compare_two_list(list_1, list_2):
    data_len = len(list_1)
    for i in range(data_len):
        if list_1[i] <= list_2[i] :
            print("Less than or equal")
        else:
            get_data.append(list_2[i])
            print("greater than")`
Giancarlo Romeo
  • 663
  • 1
  • 9
  • 24
  • 1
    Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community May 27 '22 at 12:55
-2

This code will count frequency and remove duplicate elements:

from collections import Counter

str1='the cat sat on the hat hat'

int_list=str1.split();

unique_list = []
for el in int_list:

    if el not in unique_list:
        unique_list.append(el)
    else:
        print "Element already in the list"

print unique_list

c=Counter(int_list)

c.values()

c.keys()

print c
rivermont
  • 223
  • 2
  • 12