2

I'm stuck right know because I don't know how to obtain all the possible combinations within a list of lists. I'll give you an example so that you are able to understand my problem.

I have three lists within a list [["A","B","C"],["D","E","F","G],["H","I"]]. So from these I want to obtain all the combinations but at the same time keeping this structure. Thus, one result should be:

[["A","H","G"],["D","B","F","C"],["E","I"]]

Another:

[["I","F","C"],["D","E","A","B"],["H","I"]]

so in the end every letter must be in every position of every list. If I am not making it clear please tell me.

Thank you so much

9
  • 1
    For each inner list can the elements come from any of the lists? Also can there be repetitions? Commented Mar 24, 2022 at 16:43
  • 1
    No repetitions are allowed. The letters can go from one inner list to another freely. Commented Mar 24, 2022 at 16:45
  • also can we assume that the size of the outer list is always 3? Commented Mar 24, 2022 at 16:52
  • are these: [["A","H","G"],["D","B","F","C"],["E","I"]] and [["H","G","A"],["D","B","F","C"],["E","I"]] the same outcome or different (note the position of A)? Commented Mar 24, 2022 at 16:57
  • 1
    I calculate that there are 1,260 possible combinations according to the specified rules. Is that really what you need? Commented Mar 24, 2022 at 17:14

5 Answers 5

2

Be careful computing all combinations of elements in the list because you'll get huge outputs very quickly

with that said, my method for doing this would be to:

  1. flatten your nested list
  2. generate the permutations of that flattened list
  3. re-nest those lists

the below will preserve the very specific structure in your example. if the structure changes you'll need a more general solution in the for loop.

The below is not quick, especially with a large number of permutations

import itertools
def do_the_thing(lst):
    flat_lst = list(itertools.chain(*lst))
    permutations = list(itertools.permutations(flat_lst)
    for lst in permutations:
        new = []
        new.append(lst[:3])
        new.append(lst[3:7])
        new.append(lst[7:])
Sign up to request clarification or add additional context in comments.

1 Comment

looks like you need another list outside the for loop to append new to, then return that list
2

This isn't the most "golfed" or optimized code, but it accomplishes the goal:

from itertools import permutations

l = [["A","B","C"],["D","E","F","G"],["H","I"]]

# flatten the list
flat_l = [val for l_i in l for val in l_i]

# determine where to re-segment the flattened permutations
# by tracking the original index breaks
shape = [len(l_i) for l_i in l]
for ix in range(1, len(shape)):
    shape[ix] += shape[ix-1]

final_combos = [
    [list(p[start:end]) for start, end in zip([0]+shape[:-1], shape)]
    for p in permutations(flat_l)
]

This is the same approach as @tropho, just using dynamic grouping of the original sublists. This allows for the size of the sublists to change.

5 Comments

this is impressively clean and short
@ElectronX Now it just needs to also be correct. The first two combos it produces are [['A', 'B', 'C'], ['D', 'E', 'F', 'G'], ['H', 'I']] and [['A', 'B', 'C'], ['D', 'E', 'F', 'G'], ['I', 'H']]. And they said "the order of the elements of inner lists is not important" as well as "No repetitions are allowed", so that second combo looks like a disallowed repetition of the first to me.
What's the opposite of golfing? list(permutations(flat_l, r=len(flat_l))) needlessly adds code to permutations(flat_l) (at which point it's so short and clear that I think it would also be clearer without the detour through the extra variable perms).
@KellyBundy, the question says "every letter must be in every position of every list". Where are you quoting "the order of the elements of inner lists is not important" from? Thanks for the feedback about the permutations part though. Edited to correct.
That quote is from the sixth comment under the question.
1

Let us approach the problem in the following steps:

1. Build a list of all possible allowed characters

We can do this very easily using the function below:

def flatten_list(main_list):
    all_elems = []
    for inner_list in main_list:
        for elem in inner_list:
            all_elems.append(elem)
    return list(set(all_elems))

all_elems = flatten_list(main_list)

print (all_elems)

>>>
['H', 'I', 'C', 'B', 'D', 'F', 'A', 'G', 'E']

2. Build the possible combinations of all the first inner list

There are 9 elements in total For the first list, there will be 9C3 options = 84

We can build this using combinations function as:

from itertools import combinations
first_list_candidates = list(combinations(all_elems, len(main_list[0])))

print (len(first_list_candidates ))

>>> 84

3. Build the other inner lists

Now since you do not want repetitions, the way to build the other lists is:

For each combo in first list:
    check which elems are remaining
    construct second list using these elems
    For each combo in second list
        check which elems are remaining
        construct third list using these elems
        Append the combinations into a new list

In python:

all_results = []

### for each combination in first list
for combo1_ in first_list_candidates:
    ### check which remaining elems we can pick
    remaining_elems = [elem for elem in all_elems if elem not in combo1_]
    ### get all candidates for second list from these remaining elems
    second_list_candidates = list(combinations(remaining_elems, len(main_list[1])))
    
    ### for each combination in second list
    for combo2_ in second_list_candidates:
        ### check which remaining elems we can pick
        remaining_elems_2 = [elem for elem in remaining_elems if elem not in combo2_]
        ### get all candidates for third list from these remaining elems
        third_list_candidates = list(combinations(remaining_elems_2, len(main_list[2])))

        ### append the results
        for combo3_ in third_list_candidates:
            all_results.append([combo1_, combo2_, combo3_])

As someone in the comments mentioned there are 1260 possible combinations

len(all_results)
>>>
1260

Hope this helped, I am sure there is a more optimized solution though...

Comments

0

My approach, ill just put it here even though theres already better ans

seed = [["A","B","C"],["D","E","F","G"],["H","I"]]

def all_c_in_format(seed:list):
    seed_config = [0]
    i = 0
    for val in seed:
        i+= len(val)
        seed_config.append(i)
    seed = list(itertools.chain(*seed))
    combination_list = []
    for subset in itertools.permutations(seed, len(seed)):
        listed = list(subset)
        bucket = []
        for i,L in enumerate(seed_config):
            if i < len(seed_config)-1:
                next_L = L + seed_config[i+1]
            else:
                break
            bucket.append(listed[L:next_L])
        combination_list.append(bucket)

    return combination_list

all_combination = all_c_in_format(seed)
for i in all_combination:
    print(i)

Comments

0

This could be very inefficient if the dataset was much larger and is perhaps a little cumbersome but seems to produce the right results:

from itertools import combinations, chain

master = [["A","B","C"],["D","E","F","G"],["H","I"]]
flat = set(chain(*master))

for c3 in map(list, combinations(flat, 3)):
    fc3 = flat ^ set(c3)
    for c4 in map(list, combinations(fc3, 4)):
        for c2 in map(list, combinations(fc3 ^ set(c4), 2)):
            print([c3, c4, c2])

This runs in less than 0.008s on my machine (including the print)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.