visualnotsobasic
visualnotsobasic

Reputation: 428

Itertools return value NOT used in combinations

I am using for num in combinations(nums[0], number): to return all combinations of numbers in a list, where num = len(nums[0])-1.

What I would like to do is return, as a separate variable, the value of the list item that is not used in each combination, so for example if nums[1,2,3] then I would want it to return:

[1,2],[3]   
[1,3],[2]  
[2,3],[1]  

Please tell me if this is unclear. I feel like this is probably some basic python fundamentals but I can't figure out how to do it. Thank you for any help.

Upvotes: 1

Views: 397

Answers (2)

Grismar
Grismar

Reputation: 31329

Since your list can have duplicates:

from itertools import combinations

nums = [1, 2, 3, 3]

# get combinations of all possible lengths
combos = []
for n in range(len(nums)):
    combos += combinations(nums, n)

# create the pairs you want, but with all nums
combo_pairs = [(combo, list(nums)) for combo in combos]
# remove the nums that are in the combination for each pair
for combo, combo_nums in combo_pairs:
    for n in combo:
        combo_nums.remove(n)

print(combo_pairs)

Note: this will cause duplicates for duplicate values (one for the one three, one for the other). You can get rid of those like this:

combo_pairs = list(set([(combo, tuple(combo_nums)) for combo, combo_nums in combo_pairs]))

That turns the nums in the pair into a tuple, since a tuple is hashable, but a list isn't. You can always convert back to a list, if you need to of course.

If you're only interested in combinations that have a length that's one less than the original, you can do this:

from itertools import combinations

nums = [1, 2, 3, 3]

# get combinations of correct length
combos = combinations(nums, len(nums)-1)

# create the pairs you want, but with all nums
combo_pairs = [(combo, list(nums)) for combo in combos]
# remove the nums that are in the combination for each pair
for combo, combo_nums in combo_pairs:
    for n in combo:
        combo_nums.remove(n)

print(combo_pairs)

But in that case, you may as well:

nums = [1, 2, 3, 3]
combos = [(nums[:n] + nums[n+1:], [nums[n]]) for n in range(len(nums))]

Upvotes: 3

kaya3
kaya3

Reputation: 51063

Since the rest of the combination is uniquely determined by which element is "left out", you don't really need itertools for this.

def combinations_leaving_one_out(lst):
    lst = sorted(lst)
    prev_x = None
    for i, x in enumerate(lst):
        if x != prev_x:
            yield tuple(lst[:i] + lst[i+1:]), x
        prev_x = x

Example:

>>> lst = [1, 2, 3, 1, 2, 4]
>>> for comb, left_out in combinations_leaving_one_out(lst):
...     print(comb, left_out)
... 
(1, 2, 2, 3, 4) 1
(1, 1, 2, 3, 4) 2
(1, 1, 2, 2, 4) 3
(1, 1, 2, 2, 3) 4

Upvotes: 0

Related Questions