Skip to content Skip to sidebar Skip to footer

How To Nest Itertools Products?

Given a list, I can get the product of each item in a list as such: from itertools import product x = 'apple orange pair None'.split() [i + ' ' + j for i, j in product(x, x)] [out

Solution 1:

The point lies in that a recursive procedure naturally forms a recursive pattern.

To just illustrate the idea, the 'None' is not replaced with '' because of simplicity. In the further solution, it is done for the nested patterns.

def product_combine(a, b):
  return [i + ' ' + j for i, j in product(a, b)]

# for n times of nesting
def products_combine(x, n):
  if n == 0:
    return x
  else:
    return product_combine(x, products_combine(x, n-1)) + products_combine(x, n-1)

x = 'apple orange pair None'.split()    
print(products_combine(x, 3))

If in case, you need different data types to hold your result. A bit more generic solution allows more flexible choice of output data types:

# for different types of combination
def products_combine(combine):
  def products(x, n):
    if n == 0:
      return x
    else:
      return combine(x, products(x, n-1)) + products(x, n-1)
  return products

# combine to string ('None' is replaced for nested patterns, not for initial)
def tostr(a, b):
  NoneToEmpty = lambda x: '' if x == 'None' else x
  return [' '.join(map(NoneToEmpty, (i, j))).strip() for i, j in product(a, b)]

# combine to iterator (list/tuple)
def toiter(iter_type):
  def to_thatiter(a, b):
    return [iter_type((i,))+j if isinstance(j, iter_type) else iter_type((i, j)) for i, j in product(a, b)]
  return to_thatiter

tolist=toiter(list)
totuple=toiter(tuple)

products_str=products_combine(tostr)
products_list=products_combine(tolist)
products_tuple=products_combine(totuple)

x = 'apple orange pair None'.split()
print(products_str(x, 3))
print(products_list(x, 3))
print(products_tuple(x, 3))

The general form is for any binary function f(x, x), nest it to f(x, f(x, f(x,...f(x, f(x, x)). A generic approach for not only product but arbitrary binary operation would be:

def nest(f_binary, n):
  def g(x):
    if n == 1:
      return x
    else:
      return f_binary(x, nest(f_binary, n-1)(x))
  return g

add = lambda x, y: x + y
power = lambda x,y: x**y
concatenate = lambda l1, l2: l1 + l2

x = 'apple orange pair None'.split()
print(list(nest(product, 3)(x)))
print(nest(add, 3)(5))
print(nest(power,3)(5))
print(nest(concatenate, 3)(['a','b']))

A different idea is using number of arguments instead of explicit integer N to indicate the level of nesting. It looks weird, but it works.

def nest(f):
  def expand(x, *args):
    return x if not args else f(x, expand(*args))
  return expand

products = nest(product)

x = 'apple orange pair None'.split()

# instead of giving N, you call products with number n of argument x
# indicating n levels of nesting (here: 3 x, product(x, product(x, x))
print(list(products(x, x, x)))

Post a Comment for "How To Nest Itertools Products?"