python - Concatenate list elements in every possible way - python

My issue might be quite difficult to explain (maybe that's also the reason I did not find a solution or a similar problem).
What I have is a list with some elements (in my specific case also lists).
What I want is having every possible combinations of concatenations of this list in the same order.
For example:
[[1], [2], [3], [4]] # what I have
{ # what I want
[[1], [2], [3], [4]],
[[1, 2], [3], [4]],
[[1], [2, 3], [4]],
[[1], [2], [3, 4]],
[[1, 2], [3, 4]], # Update 1
[[1, 2, 3], [4]],
[[1], [2, 3, 4]],
[[1, 2, 3, 4]]
}
In general the length of the sublists is greater then 1; also the list itself may have more than 4 elements.
Any help is highly appreciated.
UPDATE 1:
added missing combination in code.

Try this:
def concats(l):
if len(l) < 2:
return [l]
return [[l[0]] + x for x in concats(l[1:])] + \
concats([l[0] + l[1]] + l[2:])
Here's a sample case:
l = [[1], [2], [3], [4]]
r = concats(l)
And the result:
[[[1], [2], [3], [4]],
[[1], [2], [3, 4]],
[[1], [2, 3], [4]],
[[1], [2, 3, 4]],
[[1, 2], [3], [4]],
[[1, 2], [3, 4]],
[[1, 2, 3], [4]],
[[1, 2, 3, 4]]]
Edit: It wasn't clear to me how the empty list should be handled, but in that case you may want to simply return an empty list without wrapping it in an outer list - I'll leave that case up to you. A simple check at the top of the function can handle it any way you choose (and it won't affect the larger cases).

Related

Combinations using Recursion [duplicate]

I have the following python function to print all subsets of a list of numbers:
def subs(l):
if len(l) == 1:
return [l]
res = []
for sub in subs(l[0:-1]):
res.append(sub)
res.append([l[-1]])
res.append(sub+[l[-1]])
return res
li = [2, 3, 5, 8]
print(subs(li))
This returns:
[[2], [8], [2, 8], [5], [8], [5, 8], [2, 5], [8], [2, 5, 8], [3], [8], [3, 8], [5], [8], [5, 8], [3, 5], [8], [3, 5, 8], [2, 3], [8], [2, 3, 8], [5], [8], [5, 8], [2, 3, 5], [8], [2, 3, 5, 8]]
Which is not the expected answer. It looks like python takes the list l into the function by reference. So when I append l[-1], it appends the last element of original list, not the smaller list sent into the recursive method. Is there any way to solve this?
This could possibly be solved using tuples but I'm wondering if there is a solution using lists.
def subs(l):
if l == []:
return [[]]
x = subs(l[1:])
return x + [[l[0]] + y for y in x]
Results:
>>> print (subs([1, 2, 3]))
[[], [3], [2], [2, 3], [1], [1, 3], [1, 2], [1, 2, 3]]
There is a convenient Python module to help:
import itertools
def subs(l):
res = []
for i in range(1, len(l) + 1):
for combo in itertools.combinations(l, i):
res.append(list(combo))
return res
The results are:
>>> subs([1,2,3])
[[1], [2], [3], [1, 2], [1, 3], [2, 3], [1, 2, 3]]
Actually there is no problem with Python call by reference as I originally thought. In that case l[-1] would be 8 in all recursive calls. But l[-1] is 3, 5, 8 respectively in the recursive calls. This modified function solves the issue:
def subs(l):
if len(l) == 1:
return [l]
res = []
subsets = subs(l[0:-1])
res = res+subsets
res.append([l[-1]])
for sub in subsets:
res.append(sub+[l[-1]])
return res
returns:
[[2], [3], [2, 3], [5], [2, 5], [3, 5], [2, 3, 5], [8], [2, 8], [3, 8], [2, 3, 8], [5, 8], [2, 5, 8], [3, 5, 8], [2, 3, 5, 8]]
Improving on #Miguel Matos answer
def subsets(set_inp):
if set_inp == []:
return [[]]
x = subsets(set_inp[1:])
return sorted( x + [[set_inp[0]] + y for y in x])
print(subsets([1,2,3]))
using #Miguel Matos idea
we can get these in lexicographical order by,
def foo(l, p = [], d = {}):
if len(l)==0:
return [[]]
x = foo(l[:-1])
return x+ [[l[-1]] + y for y in x]
returns
[[], [1], [2], [2, 1], [3], [3, 1], [3, 2], [3, 2, 1], [4], [4, 1], [4, 2], [4, 2, 1], [4, 3], [4, 3, 1], [4, 3, 2], [4, 3, 2, 1]]
You can avoid using comprehensions or for-loops by using lambda functions and map.
I consider this a proper 'functional' powerset function in Python:
def powerSet(input):
# at tree leaf, return leaf
if len(input)==0:
return [[]];
# if not at a leaf, trim and recurse
# recursion is illustrated as follows:
# e.g. S = {1,2,3}
# S_trim = S without first element:
# {(),(2),(3),(2,3)}
# S_trim concatenated with first element:
# {(1),(1,2),(1,3),(1,2,3)}
# we keep the character sliced from front and concat it
# with result of recursion
# use map to apply concatenation to all output from powerset
leading = (input[0])
new_input = input[1:len(input)]
ps1 = list((powerSet(new_input)))
# concatenate over powerset-ed set
ps2 = map(lambda x: [leading]+x,ps1)
ps_list = list(map(lambda x: list(x),ps2))
return ps1+ ps_list
Continuing on the answers provided by #Miguel and #Abdul...the following function insures a lexicographically ordered output.
def subset(A):
if A == []:
return [[]]
sub = subset(A[:-1])
return sub + [rem + [A[-1]] for rem in sub]
tests = [[], [1], [1,2], [1,2,3]]
for test in tests:
print(subset(test))
RESULT
[[]]
[[], [1]]
[[], [1], [2], [1, 2]]
[[], [1], [2], [1, 2], [3], [1, 3], [2, 3], [1, 2, 3]]

Generate sets of subsets plus unused elements

I want a python program that takes a list of integers and returns a list of lists of tuples that contain the subsets as well as the subsets with the rest of the values not used in the first subset, essentially getting all possibilities to combine the values in the list.
[1,2,3,4]
should return
[[(1,), (2,), (3,), (4,)], [(1,), (2,3,), (4,)], [(1,), (2,) (3,4,)], [(1,), (2,4,), (3,)],[(1,), (2,3,4,)], [(1,2,), (3,), (4,)], [(1,2,), (3,4)]...and so on]
Yes. This is surprisingly easy with library more_itertools. You can use the
for loop below to write your tuple level output and discard undesired
results.
import more_itertools
s1 = set([1,2,3,4])
subsubsets = more_itertools.set_partitions(s1)
print(*subsubsets)
subsubsets = more_itertools.set_partitions(s1)
for l in subsubsets:
if l1 != sum(len(k) for k in l):
print(f"{l} non-compliant")
break
Output (added extra linebreaks)
[[1, 2, 3, 4]]
[[1], [2, 3, 4]]
[[1, 2], [3, 4]]
[[2], [1, 3, 4]]
[[1, 2, 3], [4]]
[[2, 3], [1, 4]]
[[1, 3], [2, 4]]
[[3], [1, 2, 4]]
[[1], [2], [3, 4]]
[[1], [2, 3], [4]]
[[1], [3], [2, 4]]
[[1, 2], [3], [4]]
[[2], [1, 3], [4]]
[[2], [3], [1, 4]]
[[1], [2], [3], [4]]

Understanding recursion using power set example

I have written a simple piece of code to print all subsets of a set using recursion. I have a hard time understanding the output. For example, the first line in the output shows an empty set and a singleton of 3 whereas I was expecting an empty set and singleton of 1 to be printed followed by an empty set, singleton of 1, singleton of 2 etc. However, that is not what gets printed. I do not know how to visualize recursion tree. Are there any general techniques to accomplish the visualisation? I tried drawing a tree but it quickly gets confusing.
def subsets(self, nums):
inp = nums
out = []
result=[]
def helper(inp,out,index):
if index==len(inp):
result.append(out)
return
helper(inp,out,index+1)
helper(inp,out+[inp[index]],index+1)
print(result)
helper(inp,out,0)
return result
The output from the print statement for the input '[1,2,3]' is shown below
[[], [3]]
[[], [3], [2], [2, 3]]
[[], [3], [2], [2, 3]]
[[], [3], [2], [2, 3], [1], [1, 3]]
[[], [3], [2], [2, 3], [1], [1, 3], [1, 2], [1, 2, 3]]
[[], [3], [2], [2, 3], [1], [1, 3], [1, 2], [1, 2, 3]]
[[], [3], [2], [2, 3], [1], [1, 3], [1, 2], [1, 2, 3]]
If you add an "indentation" parameter to your function, while you explore it, you can immediately see which function calls which:
def subsets(nums):
inp = nums
out = []
result=[]
def helper(indent,inp,out,index):
print(f"{indent}->helper({inp},{out},{index})")
if index==len(inp):
result.append(out)
return
helper(indent+'--',inp,out,index+1)
helper(indent+'--',inp,out+[inp[index]],index+1)
helper('',inp,out,0)
return result
The result will look like:
->helper([1, 2, 3],[],0)
--->helper([1, 2, 3],[],1)
----->helper([1, 2, 3],[],2)
------->helper([1, 2, 3],[],3)
------->helper([1, 2, 3],[3],3)
----->helper([1, 2, 3],[2],2)
------->helper([1, 2, 3],[2],3)
------->helper([1, 2, 3],[2, 3],3)
--->helper([1, 2, 3],[1],1)
----->helper([1, 2, 3],[1],2)
------->helper([1, 2, 3],[1],3)
------->helper([1, 2, 3],[1, 3],3)
----->helper([1, 2, 3],[1, 2],2)
------->helper([1, 2, 3],[1, 2],3)
------->helper([1, 2, 3],[1, 2, 3],3)
So you can immidiately see why you get [] first--you get it when you go all the way through the list without including anything in the results. You get [3] next because you backtrack to the call where you add 3 and then go to the end. You get [2] by backtracking a bit further, to where you include 2 in the output, and then down the path that doesn't add 3. Then you get [2,3] because you backtrack one level up, to the call that has 2 included in the result, and this time go to the path that adds 3.
It probably isn't the easiest way to compute a power-set, though. There is a one-to-one correspondence between the powerset of size n and the binary numbers between 0 and 2**n-1. For each number, the 1-bits indicate which elements to include in the set. So you can also compute the powerset like this:
def subsets(nums):
return [
[nums[j] for j, b in enumerate(reversed(format(i, 'b'))) if b == '1']
for i in range(2**len(nums))
]
It runs in exponential size, but so does the recursive version, and that is unavoidable when the output is exponential in the size of the input.

Trying to create array with 3 coulms by using a lsi, but can figure out how to add a row

I have created a list as follows
chart=[ [1], [2], [3] ]
when I print it, i get
[ [1], [2], [3] ]
now I try to add another row
chart.append([ [4],[5],[6]] )
[[1], [2], [3], [[4], [5], [6]]]
It added another row not column,
How can I get it so if I would get
[ [1,4], [2, 5], [3,6]]
Code:
a = [ [1], [2], [3] ]
b = [ [4],[5],[6]]
print( [i + j for i, j in zip(a, b)] )
Output:
[[1, 4], [2, 5], [3, 6]]
Straightforwardly:
Note: chart.append(...) in the question means that the initial list is expected to be modified
chart = [[1], [2], [3]]
new_cols = [[4], [5], [6]]
for i, lst in enumerate(chart):
chart[i] += new_cols[i]
print(chart) # [[1, 4], [2, 5], [3, 6]]
This code will give you the results for your above chart list [[1], [2], [3], [[4], [5], [6]]]
out = []
for i in range(len(chart)-1):
a = chart[i]
a.append(chart[-1][i][0])
out.append(a)
print(out)

Entering value in sublist if empty

So I have a larger for loop which produces nested lists of various of sizes: e.g.
[[6], [3], [5], [3, 2, 5, 3], [5], [6, 5, 4], [5, 3, 2]]
[[5], [], [], [4], [3]]
[[5], [2]]
[[], [4], [3, 2, 4]
In short, I would like it so that each array that has an empty sublist to simply be the value 0. So like :
If the list generated is :
[[6], [3], [5], [3, 2, 5, 3], [5], [6, 5, 4], [5, 3, 2]]
Then I would keep it that way.
But if the list generated is:[[5], [], [], [4], [3]]
Then I would like it to be:
[[5], [0], [0], [4], [3]]
Likewise, for the final row above, I would want it to be:[[0], [4], [3, 2, 4]
I have been trying something along the lines of :
for k in range(0,len(TempAisle)):
if TempAisle[k][0] == None:
TempAisle[k][0]= 0
k+=1
But I am getting an list index out of range error.
It seems to be a rather simple problem and have seen others who have asked ways to check if there is a sublist that is empty but I am having trouble replacing it with another value (0 in my case). Any help and explanation will be appreciated.
You can iterate on the items directly - the more Pythonic way - no need to use range. Test for the truthiness of each of the items (empty lists are falsy), and append a zero only if the sublist is empty:
for x in TempAisle:
if not x:
x.append(0)
For a one-line solution:
x = [elem or [0] for elem in TempAisle]
An EDIT based on the comment of #Moses Koledoye, to avoid creating a new list, but to keep it as a one-line solution:
[x.append(0) for x in TempAisle if not x]

Categories