简体   繁体   中英

Duplicating items in a list with combinations

With two lists in Python:

i = [0, 1, 2, 3, 4]
j = [1, 4]

I'm trying to duplicate values in j and to get all unique combinations. i can be a list of any (hashable) values. It will never contain duplicates, but the order of the values is important. j is always a subset of i . Integers are used for example only, the actual values could be strings.

The output should be:

[
[0, 1, 2, 3, 4], # i is always included as a combination - this can be added independently if required
[0, 1, 1, 2, 3, 4],
[0, 1, 1, 2, 3, 4, 4],
[0, 1, 2, 3, 4, 4]
]

An additional example for clarity:

i = ["B", "E", "C", "A", "D"]
j = ["C", "A", "D"]

# desired output - the order of i should be preserved
# but the order of lists within the outer list is unimportant
[
["B", "E", "C", "A", "D"], # original list
["B", "E", "C", "C", "A", "D"], # duplicated C
["B", "E", "C", "C", "A", "A", "D"], # duplicated C, A
["B", "E", "C", "C", "A", "D", "D"], # duplicated C, D
["B", "E", "C", "C", "A", "A", "D", "D"], # duplicated C, A, D
["B", "E", "C", "A", "A", "D"], # duplicated A
["B", "E", "E", "C", "A", "A", "D", "D"], # duplicated A, D
["B", "E", "C", "A", "D", "D"], # duplicated D
]

I've tried various list comprehension methods, and a few itertools functions such as product and combination, but have yet to find a satisfactory solution.

Half-working attempt is as follows, although it is an ugly approach:

i = [0, 1, 2, 3, 4]
j = [1, 4]

all_lists = [i]
for x in j:
    new_list = i.copy()
    new_list.insert(i.index(x), x)
    all_lists.append(new_list)

print(all_lists)
# [[0, 1, 2, 3, 4], [0, 1, 1, 2, 3, 4], [0, 1, 2, 3, 4, 4]]
# still missing the [0, 1, 1, 2, 3, 4, 4] case

Background to question - i is a list of nodes in a networkx network, and j a list of nodes with self-loops. The result is a combination of all non-simple paths between the nodes.

You're right to try to use itertools . The combinations function is what you want. Just loop over all possible lengths (from 0 to len(j)):

import itertools

i = ["B", "E", "C", "A", "D"]
j = ["C", "A", "D"]

result = []
for l in range(len(j) + 1):
    for j_subset in itertools.combinations(j, l):
        result.append(sorted(i + list(j_subset), key=lambda x: i.index(x)))


print(result)

You said that the lists can contain elements of any type. Here is an example of doing the same thing using a custom class Node :

import itertools

# here is my custom Node class
class Node():
    def __init__(self, name):
        self.name = name

    def __eq__(self, other):
        return self.name == other.name

# the names of the nodes
names_i = ["B", "E", "C", "A", "D"]
names_j = ["C", "A", "D"]


# the actual data we are working with - lists of distinct nodes
i = [Node(name) for name in names_i]
j = [Node(name) for name in names_j]

result = []

for l in range(len(j) + 1):
    for j_subset in itertools.combinations(j, l):
        result.append(sorted(i + list(j_subset), key=lambda x: i.index([node for node in i if node == x][0])))


# print the raw result
print(result)

# print the result but just showing the node names
print([[node.name for node in result_element] for result_element in result])

The key difference with using classes is that two Node classes instantiated with the same name are not the same object, so i.index(x) in your sorted key will not work, since no elements of j are in i (even though they have the same name, and are "equal"). I can explain this more if you like.

Changed to reflect that j is a list of the values to duplicate

for k in range(len(j) + 1):
    for n in itertools.combinations(j, k):
        lst = i[:]
        for el in n:
            ix = lst.index(el)
            lst.insert(ix, lst[ix])
        print(lst)

EDIT using insert is more efficient than what I had before

Is this what you're looking for?

In [1]: %paste                                                                                                                                                                                                                                                                       
from itertools import combinations

i = [0, 1, 2, 3, 4]
j = [1, 4]

res = []
for r in range(len(j) + 1):
    for comb in combinations(j, r):
        res.append(sorted(i + list(comb)))
print(res)

## -- End pasted text --
[[0, 1, 2, 3, 4], [0, 1, 1, 2, 3, 4], [0, 1, 2, 3, 4, 4], [0, 1, 1, 2, 3, 4, 4]]

In [2]: 

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