Recursively generate combinations with Python - python

I am trying to provide a recursive method that provides a list of all the possible combinations when given a list of courses. E.g course = [Entree, Dessert]
This is what I have so far:
Entree = ["pumkinsoup","antipasto"]
Dessert = ["cheesecake", "icecream", "tiramisu", "cheeseplatter"]
courses = [Entree, Dessert]
def make_orders(courses):
dishes_so_far = []
recursive_make_orders(dishes_so_far, courses)
def recursive_make_orders(dishes_so_far, courses):
n = len(courses)
if n==0 :
print(dishes_so_far)
else:
current_courses = courses[0]
for D in current_courses:
dishes_so_far.append(D)
recursive_make_orders(dishes_so_far , courses[1:len(courses)])
\I am trying to make it so that it prints out the combination like [[pumkinsoup,cheesecake],[punkinsoup, icecream]] and etc but its actually giving me [pumkinsoup, cheesecake, icecream] and so on.
Tried adding it with addition instead of append and it gave me an error.
This is homework, so a recursive method is required.

You're not too far off - use itertools.product and *courses to unpack to it:
from itertools import product
for course in product(*courses):
print course
('pumkinsoup', 'cheesecake')
('pumkinsoup', 'icecream')
('pumkinsoup', 'tiramisu')
('pumkinsoup', 'cheeseplatter')
('antipasto', 'cheesecake')
('antipasto', 'icecream')
('antipasto', 'tiramisu')
('antipasto', 'cheeseplatter')

If you want recursive version, you can do something like this:
def worker(entree, dessert):
d = []
if not entree or not dessert: return d
d.append((entree[0], dessert[0]))
d += worker(entree[1:], dessert)
d += worker(entree, dessert[1:])
return d
Your version is not working as you said because courses now a list of lists, and courses[0] is just Entree, so you recursively constructiong new list from Entree.

Related

How to dynamically assign an unknown number of list elements?

I have this code which works but it's a big function block of IF..ELIF..ELSE. Is there a way to unpack or dynamically assign two lists. The thing is, sometimes mylist could have less elements than 4.
Input:
mylist=['1','2','3','4']
flag=['a','b','c','d']
Output:
A string object like 'a=1/b=2/c=3/d=4/' OR 'a=1/b=2/c=3/' if mylist only has 3 elements.
My current method is just like:
def myoutput(mylist, flag):
if flag=='3':
out = f'a={mylist[0]}/b={mylist[1]}/c={mylist[2]}/'
else:
out = f'a={mylist[0]}/b={mylist[1]}/c={mylist[2]}/d={mylist[3]}/'
return out
I tried to zip the two list, but I do not know the next steps and it doesn't really work:
tag_vars={}
for i in range((zip(mylist,flag))):
tag_vars[f"mylist{i}"] = flag[i]
print(tag_vars)
I would use zip for this task following way
mylist=['1','2','3','4']
flag=['a','b','c','d']
out = ''.join(f'{f}={m}/' for f,m in zip(flag,mylist))
print(out)
output
a=1/b=2/c=3/d=4/
Note that I used f,m with zip so f and m are corresponding elements from flag and mylist. Disclaimer: this solution assumes flag and mylist have always equal lengths.
You can use zip like this:
for a, b in zip(alist, blist): ...
I modified myoutput function to return your desired output
mylist=['1','2','3','4']
flag=['a','b','c','d']
def myoutput(mylist, flag):
result = []
for elem, f in zip(mylist, flag):
result.append(f"{f}={elem}")
return "/".join(result)
print(myoutput(mylist, flag)) # a=1/b=2/c=3/d=4
print(myoutput(mylist[:3], flag)) # a=1/b=2/c=3

Python how do I append to every string in list using lambda function?

I'm trying to learn about lambda functions, I'm would like the code to:
add a slash '/' to the end of a string in list 'techCodeList'
add and append every item in 'finishList' to the every entry in 'techCodeList', appending to 'combinedCodeList' every time (so combinedCodeList = ['a/ABUL', 'a/BEL', 'a/PBL'] etc)
I could do it using other methods but I want to try using lambda, so would it be viable and if so how would I do it? My code is below:
#example shortened
class myClass:
def __init__(self):
self.techCodeList = ('a','b','c')
def applyCodes(self):
self.combinedCodeList = list()
finishList = ["ABUL","BEL","PBL","PBUL","ABL","SBL","SBSL","SBUL","PNP","SNP","PCP","SCP","NBP","ASP","ACP","SAL","SAS","AMB","CBP","HBN","MBL","MWL","HBB","SPE","PBUL/SAMPLE"]#list to append to techcodelist
len1 = len(self.combinedCodeList)
arrayCounter = 0
for i in self.techCodeList:
for _ in finishList:
print (arrayCounter)
self.techCodeList = list(map(lambda orig_string: orig_string + '/', self.techCodeList[arrayCounter]))
self.techCodeList = list(map(lambda orig_string: orig_string + finishList[arrayCounter], self.techCodeList[arrayCounter]))
self.combinedCodeList.append(self.techCodeList[arrayCounter])
if arrayCounter == len(self.techCodeList) - 1:
break
arrayCounter = arrayCounter + 1
print (self.combinedCodeList)
myClass()
And here is the result in the combinedCodeList:
['aABUL', '/BEL', '/BEL', '/BEL']
If you have any other suggestions for good habits or suggestions for my code please feel free to leave them too, I'm still very much learning. Thanks.
If I understood correctly you want to create an exchaustive combinations between all the entries from the tech code and finish lists.
For that you can use list comprehension like below:
tech_code_list = ["a", "b", "c"]
finish_list = ["ABUL","BEL","PBL","PBUL","ABL","SBL","SBSL","SBUL","PNP","SNP","PCP","SCP","NBP","ASP","ACP","SAL","SAS","AMB","CBP","HBN","MBL","MWL","HBB","SPE","PBUL/SAMPLE"]
combined_code_list = [
tech_code + "/" + finish for tech_code in tech_code_list for finish in finish_list
]
print(combined_code_list)
# will print: ['a/ABUL', 'a/BEL', 'a/PBL', 'a/PBUL', ... ]
iterools.product gives you all pairwise combinations from two lists. As you want to use lambda we do that in the map function
from itertools import product
list(map(lambda p: p[0] + '/' + p[1], product(tech_code_list, finish_list)))
output:
['a/ABUL',
'a/BEL',
'a/PBL',
'a/PBUL',
'a/ABL',
...
]

Python - Im trying to make a function that would add "*" in between each letter

I have this:
def f(message):
l = []
for c in message:
l.append(c)
l.append('*')
return "".join(l)
It works but how do I make it so that it doesn't add "*" at the end. I only want it to be between the inputted word. I'm new to python and was just trying new things.
May be you can try this. It uses list comprehension
input_str = 'dog'
def f(x):
return '*'.join(x)
print(f('dog')) #ouput d*o*g
print(f(input_str)) #ouput d*o*g
Well, technically you could just slice the returned string cutting off the last astrix.
message="dog"
def f(message):
l = []
for c in message:
l.append(c)
l.append('*')
return "".join(l[:-1])
print(f(message))
this way it returns
d*o*g

Recursively Generating a List of n choose k combinations in Python - BUT return a list

I'm attempting to generate all n choose k combinations of a list (not checking for uniqueness) recursively by following the strategy of either include or not include an element for each recursive call. I can definitely print out the combinations but I for the life of me cannot figure out how to return the correct list in Python. Here are some attempts below:
class getCombinationsClass:
def __init__(self,array,k):
#initialize empty array
self.new_array = []
for i in xrange(k):
self.new_array.append(0)
self.final = []
self.combinationUtil(array,0,self.new_array,0,k)
def combinationUtil(self,array,array_index,current_combo, current_combo_index,k):
if current_combo_index == k:
self.final.append(current_combo)
return
if array_index >= len(array):
return
current_combo[current_combo_index] = array[array_index]
#if current item included
self.combinationUtil(array,array_index+1,current_combo,current_combo_index+1,k)
#if current item not included
self.combinationUtil(array,array_index+1,current_combo,current_combo_index,k)
In the above example I tried to append the result to an external list which didn't seem to work. I also tried implementing this by recursively constructing a list which is finally returned:
def getCombinations(array,k):
#initialize empty array
new_array = []
for i in xrange(k):
new_array.append(0)
return getCombinationsUtil(array,0,new_array,0,k)
def getCombinationsUtil(array,array_index,current_combo, current_combo_index,k):
if current_combo_index == k:
return [current_combo]
if array_index >= len(array):
return []
current_combo[current_combo_index] = array[array_index]
#if current item included & not included
return getCombinationsUtil(array,array_index+1,current_combo,current_combo_index+1,k) + getCombinationsUtil(array,array_index+1,current_combo,current_combo_index,k)
When I tested this out for the list [1,2,3] and k = 2, for both implementations, I kept getting back the result [[3,3],[3,3],[3,3]]. However, if I actually print out the 'current_combo' variable within the inner (current_combo_index == k) if statement, the correct combinations print out. What gives? I am misunderstanding something to do with variable scope or Python lists?
The second method goes wrong because the line
return [current_combo]
returns a reference to current_combo. At the end of the program, all the combinations returned are references to the same current_combo.
You can fix this by making a copy of the current_combo by changing the line to:
return [current_combo[:]]
The first method fails for the same reason, you need to change:
self.final.append(current_combo)
to
self.final.append(current_combo[:])
Check this out: itertools.combinations. You can take a look at the implementation as well.

How to apply n-times a function with map

I am starting to learn how to use python in a functional way and I am facing a problem I can't solve.
I have the following code (take in part from this question) which does exactly what I want:
url = "www.testpage.com/"
def gen_url(prefix, suffix, places=3):
pattern = "{}{{:0{}d}}{}".format(prefix, places, suffix)
for i in count(1):
yield pattern.format(i)
list_of_urls = []
for c in "xyz":
g = gen_url(url+c,"&show=more")
for x in range(2):
list_of_urls.append(next(g))
And it generates something like this:
www.testpage.com/x001&show=more
www.testpage.com/y001&show=more
www.testpage.com/z001&show=more
www.testpage.com/x002&show=more
www.testpage.com/y002&show=more
www.testpage.com/z002&show=more
As you can see, It stops at 002 because of:
...
for x in range(2):
list_of_urls.append(next(g))
...
All the time I start with an empy list, use a for loop and fill it. I am trying to use map in this way and get rid of the for loop:
urls = map(lambda x:next(gen_url(url+x,"&show=more")),"xyz")
And it works. But I can only get to 001. Let's assume I want to reach 002; I am trying something like the following, but It doesn't work:
urls = imap((lambda x:next(gen_url(url+x,"&show=more")),"xyz"),2)
And this as well doesn't work:
urls = map((lambda x:next(gen_url(url+x,"&show=more")),"xyz"),repeat(2))
Could somebody explain me how to properly use the iterators in this case?
Functionally it would look like this:
def gen_url(prefix, suffix, id, places=3):
pattern = "{}{{:0{}d}}{}".format(prefix, places, suffix)
return pattern.format(id)
url = "www.testpage.com/"
a = [ gen_url(url + l, "&show=more", n) for l in "xyz" for n in range(1,4) ]
print a
So now your gen_url is a pure function that accepts everything from outside.
And your're generating a cartesian product (basically all permutations) of 2 sequences "xyz" and [1, 2, 3]
The script above generates:
['www.testpage.com/x001&show=more',
'www.testpage.com/x002&show=more',
'www.testpage.com/x003&show=more',
'www.testpage.com/y001&show=more',
'www.testpage.com/y002&show=more',
'www.testpage.com/y003&show=more',
'www.testpage.com/z001&show=more',
'www.testpage.com/z002&show=more',
'www.testpage.com/z003&show=more']
Prefix and suffix are detracting from the simple logic in gen_url.
They can be pulled out.
Try this:
from itertools import count, islice
def gen_url(places=3):
for i in count(1):
yield "{{:0{}d}}".format(places).format(i)
url = "www.testpage.com/"
list_of_urls = [url+c+x+"&show=more" for c in "xyz" for x in islice(gen_url(), 0, 2)]

Categories