简体   繁体   中英

Generate Unique Pairs From Two Lists Without Itertools

I am looping over a list twice and want to catch all unique pairs, rather than all combinations- ie. order in the pair doesn't matter

listy=[0,1,2]
out=[]
for i in listy:
   for j in listy:
      out.append([i,j])

The output I'm getting is [[0,0],[0,1],[0,2],[1,0],[1,1],[1,2],[2,0],[2,1],[2,2]]

What I am looking for is [[0,0],[0,1],[0,2],[1,1],[1,2],[2,2]]

One possible solution is,

listy=[0,1,2]
out=[]
for i in listy:
   for j in listy:
      pair=set([i,j])
      if pair not in out:
          out.append(pair)

This produces [{0},{0,1},{0,2},{1},{1,2},{2}]

However this creates inefficiency in what is a heavy script (the lists are long) and I also do not want a list of sets. I want a list of lists.

Is there a better way to achieve this without using itertools (I want an implementation I can also apply to javascript without too much rethinking)

Option 1 - "minor fix"

A trivial "fix" would be:

listy=[0,1,2]
out=set()
for i in listy:
    for j in listy:
        if (i, j) not in out and (j, i) not in out:
            out.add((i,j))

The result is:

{(0, 1), (1, 2), (0, 0), (1, 1), (0, 2), (2, 2)}

However this is not an efficient implementation, because we have to check twice if an element is in the list.

Option 2 - More efficient implementation

You could achieve your goal using a trivial scan of the array:

listy=[0,1,2]
out = [(i, j) for i in range(len(listy)) for j in range(i, len(listy))]

NOTE: I use tuples for the pairs, you could easily change that into a list of lists using:

out = [[i, j] for i in range(len(listy)) for j in range(i, len(listy))]

I don't know javascript at all, but if there is something analogous to list comprehension I would try this:

listy = [0,1,2]
pairs = [[i, j] for i in listy for j in listy if i <= j]

The content of pairs is exactly as you wanted:

[[0, 0], [0, 1], [0, 2], [1, 1], [1, 2], [2, 2]]

The most straightforward translation of itertools.combinations_with_replacement(listy, 2) (which matches the behavior you desire) that directly generates the results you want from an input list is:

listy = [0,1,2]
out = []
for idx, i in enumerate(listy):
   for j in listy[idx:]:
      out.append([i, j])

Only change is the use of enumerate (to get the current index being iterated as you iterate) and slicing the listy used in the inner loop (so it starts at the same index as the current run of the outer loop).

This gets the exact result requested with minimal overhead (it does make shallow copies of the list , of decreasing size, once for each inner loop, but this is fairly quick in Python; unless the list is huge, it should be a pretty minimal cost). If you need to avoid slicing, you can make the inner loop an index-based loop with indexing (but in practice, the overhead of indexing is high enough that it'll often lose to the slicing):

listy = [0,1,2]
out = []
for idx, i in enumerate(listy):
   for idxj in range(idx, len(listy)):
      out.append([i, j[idxj]])

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM