Search code examples
pythonpython-itertools

Apply function to all combinations of two lists using map()


I am trying to apply a function to all combinations of lists. I am trying to avoid using for loops as in the program I am trying to write I would end up with 5 or more nested loops. One suggestion I had was to use the map() function, but I don't seem to be able to make it work.

Some examples:

For the sake of simplicity, I have a function which removes a hyphen from a string, and adds some suffix on the end of the string:

def changeString(item, suffix):
    foo = item.replace("-", "")
    bar = foo + suffix
    return bar

items = ["Hel-lo", "Wor-ld", "-test-"]
suffixes = ["41", "2", "5"]

To achieve my desired behaviour, I can use:

foobar = []
for item in items:
    for suffix in suffixes:
        foobar.append(changeString(item, suffix))

which gives the output:

['Hello41', 'Hello2', 'Hello5', 'World41', 'World2', 'World5', 'test41', 'test2', 'test5']

This is the output I want, but I don't want to keep nesting loops like this.

I have tried to use map() to avoid nesting for loops, which looks like this:

foobar = list( map( changeString, items, suffixes ) )

But this gives the wrong output:

['Hello41', 'World2', 'test5']

I have also attempted to use itertools.product as such:

lst = [items, suffixes]
foobar = list(itertools.product(*lst))

but this gives the output:

[('Hel-lo', '41'), ('Hel-lo', '2'), ('Hel-lo', '5'), ('Wor-ld', '41'), ('Wor-ld', '2'), ('Wor-ld', '5'), ('-test-', '41'), ('-test-', '2'), ('-test-', '5')]

which to me seems like I have to use more loops to allow it be to be used by the function I defined above.

What is the most efficient way to apply my function to every possible combination of strings, while avoiding nesting for loops over and over?


Solution

  • Have you tried applying changeString in a list comprehension over each product?

    from itertools import product
    
    [changeString(*p) for p in product(items, suffixes)]
    # ['Hello41', 'Hello2', 'Hello5', 'World41', ..., 'test2', 'test5']
    

    Or, for better readability,

    [changeString(item, suffix) for (item, suffix) in product(items, suffixes)]
    # ['Hello41', 'Hello2', 'Hello5', 'World41', ..., 'test2', 'test5']