How to combine list of objects into list of tuples (matrix style) in Python?

Question:

Let’s say that I have a list of tuples foo (object, integer) and a list of objects bar. The length of each of these parent lists is variable. How do I combine them into a single list of tuples, wherein each tuple is a unique combination of elements from each of the two parent lists?

  • Example 1:

    foo = [(A, i)]
    bar = [X, Y]
    # missing magic
    expected_result = [(A, i, X), (A, i, Y)]
    
  • Example 2:

    foo = [(A, i), (B, j)]
    bar = [X, Y]
    # missing magic
    expected_result = [(A, i, X), (A, i, Y), (B, j, X), (B, j, Y)]
    
  • Example 3:

    foo = [(A, i), (B, j), (C, k)]
    bar = [X, Y]
    # missing magic
    expected_result = [(A, i, X), (A, i, Y), (B, j, X), (B, j, Y), (C, k, X), (C, k, Y)]
    
  • Example 4:

    foo = [(A, i), (B, j)]
    bar = [X]
    # missing magic
    expected_result = [(A, i, X), (B, j, X)]
    

Preferably, I’d like to do this in one line, if possible. The order of the resulting list is unimportant, but the order of the elements within the tuples of that list need to be predictable.

In addition to trying various solutions using itertools.product and zip, I also looked at the answers to other similar questions, but I was unable to find the solution to this problem.

Asked By: Jim Fell

||

Answers:

You can use a list comprehension with nested loops, spreading the elements in foo:

[(*f, b) for f in foo for b in bar]
Answered By: e-motta

Your case is indeed tricky, and zip or itertools.product do not do the job, since you want to concatenate a tuple and an element into a tuple with all elements.
In one line, you can use unpacking:

expected_result = [(*i, j) for i in foo for j in bar]

Assuming, as in your example, that foo is a list of tuples and bar is a list of elements.

Answered By: Gugu72

You can use the product function in the itertools library to do this a couple of different ways:

import itertools

foo = [("A", 1), ("B", 2)]
bar = ["X", "Y"]

# Iterating over the product generator with a for loop
magicOne = []
for d in itertools.product(foo, bar):
    magicOne.append( (*d[0], d[1]) )

# Using a list comprehension
magicTwo = [ (*d[0], d[1]) for d in itertools.product(foo, bar) ]

print(magicOne)
print(magicTwo)

Both of these print:
[('A', 1, 'X'), ('B', 2, 'X')]

Brief Explanation

The product function is part of the standard library and it gives you a cartesian product of whatever iterables you provide.

It returns a generator that you can then use in a loop context, such as within a for loop or a list comprehension.

This line: (*d[0], d[1]) is key, as it unpacks the first item in the product (the tuple), and combines it with the second item in the product (the list item), and combines them together into a new tuple.

Note that I used strings and ints here to keep things self-contained, but this works just as well if you have objects called A and B etc… defined elsewhere and use:

foo = [(A, i), (B, j)]
bar = [X]
Answered By: lfalin
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.