Recursively find all combinations of list - python

Problem statement
I want to get all possible combinations out of my list (including the empty list).
My code so far is:
def combination(l):
result = []
for item in range(len(l)):
cut_list = l[:item] + l[item + 1:]
if len(cut_list) > 1:
combination(cut_list)
elif len(cut_list) == 1:
result += cut_list
return result
print(combination([1, 2, 3]))
My output is an empty List
[]
i want this Output:
[[], [1], [2], [3], [1, 2], [1, 3], [2, 3], [1, 2, 3]]
I am pretty sure something with my return is not right.
Any help is extremely appreciated.

A recurrence relation can be found this way: "A combination of list l either uses the last element of l, or it doesn't."
So we find recursively the combinations of sublist l[:-1] (the sublist containing all elements except the last one); and then we either add or don't add the last element.
Recursive version
This recursion needs a base case. The base case is: if the list is empty, then the only combination is the empty combination.
def combinations(l):
if l:
result = combinations(l[:-1])
return result + [c + [l[-1]] for c in result]
else:
return [[]]
print(combinations([1,2,3]))
# [[], [1], [2], [1, 2], [3], [1, 3], [2, 3], [1, 2, 3]]
Iterative version
The recurrence relation is great, but there is no need for recursion; for-loops work very well to apply recurrence relations repeatedly.
def combinations(l):
result = [[]]
for x in l:
result = result + [c + [x] for c in result]
return result
print(combinations([1,2,3]))
# [[], [1], [2], [1, 2], [3], [1, 3], [2, 3], [1, 2, 3]]

Try this:
In [24]: import itertools
In [25]: l
Out[25]: [1, 2, 3]
In [26]: [sublist for item in [[list(x) for x in itertools.combinations(l,n)] for n in range(len(l)+1)] for sublist in item]
Out[26]: [[], [1], [2], [3], [1, 2], [1, 3], [2, 3], [1, 2, 3]]

Here's how I would do it (as a generator):
def combinations(aList):
yield []
for i,v in enumerate(aList,1):
yield from ([v]+c for c in combinations(aList[i:]))
for combo in combinations([1,2,3]): print(combo)
[]
[1]
[1, 2]
[1, 2, 3]
[1, 3]
[2]
[2, 3]
[3]
or as a list builder:
def combinations(aList):
return [[]] + [ [v]+c for i,v in enumerate(aList,1)
for c in combinations(aList[i:]) ]
print( combinations([1,2,3]) )
[[], [1], [1, 2], [1, 2, 3], [1, 3], [2], [2, 3], [3]]

this is what you wanted to do?
l = [3, 22, 10, 15, 32, 10, 5]
def f(ml: list):
a = []
for i1 in ml:
for i2 in ml:
if not i1 + i2 in a:
a.append(i1 + i2)
return a
print(f(l))
[6, 25, 13, 18, 35, 8, 44, 32, 37, 54, 27, 20, 42, 15, 30, 47, 64, 10]

You should pass your result list as well, like below. But I think this recursive function doesn't do that what you want to do.
def combination(l, result=[]):
for item in range(len(l)):
cut_list = l[:item] + l[item + 1:]
if len(cut_list) > 1:
combination(cut_list, result)
elif len(cut_list) == 1:
result += cut_list
return result
print(combination([3, 22, 10, 15, 32, 10, 5]))
Result:
>>> python3 test.py
[5, 10, 5, 32, 10, 32, 5, 10, 5, 15, 10, 15, 5, 32, 5, 15, 32, ...
You can get the all combinations/permutations without recursion:
Permutations:
import itertools
stuff = [1, 2, 3]
for L in range(0, len(stuff)+1):
for subset in itertools.permutations(stuff, L):
print(subset)
Output:
>>> python3 test.py
()
(1,)
(2,)
(3,)
(1, 2)
(1, 3)
(2, 1)
(2, 3)
(3, 1)
(3, 2)
(1, 2, 3)
(1, 3, 2)
(2, 1, 3)
(2, 3, 1)
(3, 1, 2)
(3, 2, 1)
Combinations (This mechanism matches with your description in your question):
import itertools
stuff = [1, 2, 3]
for L in range(0, len(stuff)+1):
for subset in itertools.combinations(stuff, L):
print(subset)
Output:
>>> python3 test.py
()
(1,)
(2,)
(3,)
(1, 2)
(1, 3)
(2, 3)
(1, 2, 3)
EDIT:
Of course, you can make a function from my below example and you can get the result as a nested list.
Code:
import itertools
def combination(l):
result = []
for L in range(0, len(l)+1):
for subset in itertools.combinations(l, L):
result.append(list(subset))
return result
print(combination([1, 2, 3]))
Output:
>>> python3 test.py
[[], [1], [2], [3], [1, 2], [1, 3], [2, 3], [1, 2, 3]]
EDIT_2:
Solution without itertools module:
Code:
def combinations(return_len, iterable):
if not return_len:
return [[]]
if not iterable:
return []
head = [iterable[0]]
tail = iterable[1:]
new_comb = [head + list_ for list_ in combinations(return_len - 1, tail)]
return new_comb + combinations(return_len, tail)
input_list = [1, 2, 3]
result = []
for n in range(0, len(input_list) + 1):
for single_result in combinations(n, input_list):
result.append(single_result)
print(result)
Output:
>>> python3 test.py
[[], [1], [2], [3], [1, 2], [1, 3], [2, 3], [1, 2, 3]]

Related

Generates the power set of the set without using pre-defined library?

I am trying to generate the power set of the list,without using any library.
For example, given the set {1, 2, 3}, it should return {{}, {1}, {2}, {3}, {1, 2}, {1, 3}, {2, 3}, {1, 2, 3}}
Note: We can use list also for it.
I have implemented the code , but it is not dynamic , it is for the specific length . How can I make it dynamic.
Code:
my_list = [0, 1, 2, 3]
first_list = []
second_list = [[]]
final_list = []
for iterate in my_list:
second_list.append([iterate])
for outer_loop in my_list:
for inner_loop in my_list:
if outer_loop!=inner_loop:
first_list.append([outer_loop, inner_loop])
result = (second_list + first_list)
for iterated in result:
if sorted(iterated) not in final_list:
final_list.append(iterated)
final_list + [my_list]
Try this:
def get_powerset(s):
x = len(s)
subsets = []
for i in range(1 << x):
subsets.append([s[j] for j in range(x) if (i & (1 << j))])
return subsets
lists = [[1, 2, 3], [0, 1, 2, 3]]
for num_list in lists:
print(get_powerset(num_list))
Output:
[[], [1], [2], [1, 2], [3], [1, 3], [2, 3], [1, 2, 3]]
[[], [0], [1], [0, 1], [2], [0, 2], [1, 2], [0, 1, 2], [3], [0, 3], [1, 3], [0, 1, 3], [2, 3], [0, 2, 3], [1, 2, 3], [0, 1, 2, 3]]
In addition to #Sabil's answer, a recursive approach:
def get_powerset(s):
if len(s) == 0:
return []
if len(s) == 1:
return [s]
without_s0 = get_powerset(s[1:])
with_s0 = [subs + [s[0]] for subs in without_s0]
return with_s0 + without_s0
This solution assumes a item method, but that can be easily relaxed.

Join list of lists into a delimiter separated list of lists

I have the following list of lists and a delimiter:
lsts = [[1, 2, 3], [4, 5], [6]]
delim = ['delim']
I'd like to mimic the string.join() behavior.
expected output is:
lst = [[1, 2, 3], ['delim'], [4, 5], ['delim'], [6]]
I tried:
from itertools import chain
n = 2
ele = 'x'
lst = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
list(chain(*[lst[i:i+n] + [ele] if len(lst[i:i+n]) == n else lst[i:i+n] for i in xrange(0, len(lst), n)]))
# which flattens the list and then inserst elements every second position,
# which doesn't help if the lists inside the list are of different length.
Try this:
lsts = [[1, 2, 3], [4, 5], [6]]
delim = ['delim']
place_holder = []
for i in lsts:
place_holder.append(i)
place_holder.append(delim)
place_holder.pop() # Pop the last element :)
print(place_holder)
You can use slice assignment
def list_join(seq, delim):
res = [delim]*(len(seq) * 2 - 1)
res[::2] = seq
return res
delim = ['delim']
# empty list
lsts = [[]]
print(list_join(lsts, delim))
# even length
lsts = [[1, 2, 3], [4, 5], [6], [7]]
print(list_join(lsts, delim))
# odd length
lsts = [[1, 2, 3], [4, 5], [6]]
print(list_join(lsts, delim))
Output
[[]]
[[1, 2, 3], ['delim'], [4, 5], ['delim'], [6], ['delim'], [7]]
[[1, 2, 3], ['delim'], [4, 5], ['delim'], [6]]
Here is my solution. It looks like the shortest code.
for i in range(len(lsts) - 1, 0, -1):
lsts.insert(i, delim)
print(lsts)
This uses a simple for loop that validates the position on the item isn’t the last item before adding the delimiter.
lsts = [[1, 2, 3], [4, 5], [6]]
delim = ['delim']
output = []
for i, l in enumerate(lsts):
output.append(l)
if i < (len(lsts) - 1):
output.append(delim)
After trying to solve it for quite a while and reviewing the other answers, I came up with an other alternative as well.
lsts = [[1, 2, 3], [4, 5], [6]]
delim = ['delim']
[x for t in zip(lsts,[delim]*len(lsts)) for x in t][:-1]
# Out[44]: [[1, 2, 3], ['delim'], [4, 5], ['delim'], [6]]

How to pack consecutive duplicates of list elements into sublists?

How can I "pack" consecutive duplicated elements in a list into sublists of the repeated element?
What I mean is:
l = [1, 1, 1, 2, 2, 3, 4, 4, 1]
pack(l) -> [[1,1,1], [2,2], [3], [4, 4], [1]]
I want to do this problem in a very basic way as I have just started i.e using loops and list methods. I have looked for other methods but they were difficult for me to understand
For removing the duplicates instead of packing them, see Removing elements that have consecutive duplicates
You can use groupby:
from itertools import groupby
def pack(List):
result = []
for key, group in groupby(List):
result.append(list(group))
return result
l = [1, 1, 1, 2, 2, 3, 4, 4, 1]
print(pack(l))
Or one-line:
l = [1, 1, 1, 2, 2, 3, 4, 4, 1]
result = [list(group) for key,group in groupby(l)]
# [[1, 1, 1], [2, 2], [3], [4, 4], [1]]
You can use:
lst = [1, 1, 1, 2, 2, 3, 4, 4, 1]
# bootstrap: initialize a sublist with the first element of lst
out = [[lst[0]]]
for it1, it2 in zip(lst, lst[1:]):
# if previous item and current one are equal, append result to the last sublist
if it1 == it2:
out[-1].append(it2)
# else append a new empty sublist
else:
out.append([it2])
Output:
>>> out
[[1, 1, 1], [2, 2], [3], [4, 4], [1]]
This code will do:
data = [0,0,1,2,3,4,4,5,6,6,6,7,8,9,4,4,9,9,9,9,9,3,3,2,45,2,11,11,11]
newdata=[]
for i,l in enumerate(data):
if i==0 or l!=data[i-1]:
newdata.append([l])
else:
newdata[-1].append(l)
#Output
[[0,0],[1],[2],[3],[4,4],[5],[6,6,6],[7],[8],[9],[4,4],[9,9,9,9,9],[3,3],[2],[45],[2],[11,11,11]]

Python groupby to split list by delimiter

I am pretty new to Python (3.6) and struggling to understand itertools groupby.
I've got the following list containing integers:
list1 = [1, 2, 0, 2, 3, 0, 4, 5, 0]
But the list could also be much longer and the '0' doesn't have to appear after every pair of numbers. It can also be after 3, 4 or more numbers. My goal is to split this list into sublists where the '0' is used as a delimiter and doesn't appear in any of these sublists.
list2 = [[1, 2], [2, 3], [4, 5]]
A similar problem has been solved here already:
Python spliting a list based on a delimiter word
Answer 2 seemed to help me a lot but unfortunately it only gave me a TypeError.
import itertools as it
list1 = [1, 2, 0, 2, 3, 0, 4, 5, 0]
list2 = [list(group) for key, group in it.groupby(list1, lambda x: x == 0) if not key]
print(list2)
File "H:/Python/work/ps0001/example.py", line 13, in
list2 = [list(group) for key, group in it.groupby(list, lambda x: x == '0') if not key]
TypeError: 'list' object is not callable
I would appreciate any help and be very happy to finally understand groupby.
You were checking for "0" (str) but you only have 0 (int) in your list. Also, you were using list as a variable name for your first list, which is a keyword in Python.
from itertools import groupby
list1 = [1, 2, 0, 2, 7, 3, 0, 4, 5, 0]
list2 = [list(group) for key, group in groupby(list1, lambda x: x == 0) if not key]
print(list2)
This should give you:
[[1, 2], [2, 7, 3], [4, 5]]
In your code, you need to change lambda x: x == '0' to lambda x: x == 0, since your working with a list of int, not a list of str.
Since others have shown how to improve your solution with itertools.groupby, you can also do this task with no libraries:
>>> list1 = [1, 2, 0, 2, 3, 0, 4, 5, 0]
>>> zeroes = [-1] + [i for i, e in enumerate(list1) if e == 0]
>>> result = [list1[zeroes[i] + 1: zeroes[i + 1]] for i in range(len(zeroes) - 1)]
>>> print(result)
[[1, 2], [2, 3], [4, 5]]
You can use regex for this:
>>> import ast
>>> your_list = [1, 2, 0, 2, 3, 0, 4, 5, 0]
>>> a_list = str(your_list).replace(', 0,', '], [').replace(', 0]', ']')
>>> your_result = ast.literal_eval(a_list)
>>> your_result
([1, 2], [2, 3], [4, 5])
>>> your_result[0]
[1, 2]
>>>
Or a single line solution:
ast.literal_eval(str(your_list).replace(', 0,', '], [').replace(', 0]', ']'))
You could do that within a Loop as depicted in the commented Snippet below:
list1 = [1, 2, 0, 2, 3, 0, 4, 5, 0]
tmp,result = ([],[]) # tmp HOLDS A TEMPORAL LIST :: result => RESULT
for i in list1:
if not i:
# CURRENT VALUE IS 0 SO WE BUILD THE SUB-LIST
result.append(tmp)
# RE-INITIALIZE THE tmp VARIABLE
tmp = []
else:
# SINCE CURRENT VALUE IS NOT 0, WE POPULATE THE tmp LIST
tmp.append(i)
print(result) # [[1, 2], [2, 3], [4, 5]]
Effectively:
list1 = [1, 2, 0, 2, 3, 0, 4, 5, 0]
tmp,result = ([],[]) # HOLDS A TEMPORAL LIST
for i in list1:
if not i:
result.append(tmp); tmp = []
else:
tmp.append(i)
print(result) # [[1, 2], [2, 3], [4, 5]]
Use zip to return a tuple of lists and convert them to list later on
>>> a
[1, 2, 0, 2, 3, 0, 4, 5, 0]
>>> a[0::3]
[1, 2, 4]
>>> a[1::3]
[2, 3, 5]
>>> zip(a[0::3],a[1::3])
[(1, 2), (2, 3), (4, 5)]
>>> [list(i) for i in zip(a[0::3],a[1::3])]
[[1, 2], [2, 3], [4, 5]]
Try to use join and then split by 0
lst = [1, 2, 0, 2, 3, 0, 4, 5, 0]
lst_string = "".join([str(x) for x in lst])
lst2 = lst_string.split('0')
lst3 = [list(y) for y in lst2]
lst4 = [list(map(int, z)) for z in lst3]
print(lst4)
Running on my console:

split a list by a lambda function in python

Is there any version of split that works on generic list types? For example, in Haskell
Prelude> import Data.List.Split
Prelude Data.List.Split> splitWhen (==2) [1, 2, 3]
[[1],[3]]
Nope. But you can use itertools.groupby() to mimic it.
>>> [list(x[1]) for x in itertools.groupby([1, 2, 3], lambda x: x == 2) if not x[0]]
[[1], [3]]
One more solution:
output = [[]]
valueToSplit = 2
data = [1, 2, 3, 4, 1, 2, 3, 4, 5, 6, 2, 5, 2]
for i, val in enumerate(data):
if val == valueToSplit and i == len(data)-1:
break
output.append([]) if val == valueToSplit else output[-1].append(val)
print output # [[1], [3, 4, 1], [3, 4, 5, 6], [5]]
You can also create an iterator and use itertools.takewhile to include all matching items and discard the delimiter:
>>> import itertools
>>> l = [1, 2, 3]
>>> a = iter(l)
>>> [[_]+list(itertools.takewhile(lambda x: x!=2, a)) for _ in a]
[[1], [3]]
#TigerhawkT3 answer isn't flawlessly, but i can't add a comment
when l = [1, 2, 2, 2, 2, 3, 4]
output [[1], [2], [2, 3, 4]]
seem "wrong", but he inspired me
import itertools
l = [1, 2, 2, 2, 2, 3, 4]
a = iter(l)
output = [[_] + list(itertools.takewhile(lambda x: x != 2, a)) for _ in a if _ != 2]
output = [[1], [3, 4], [123, 123]]

Categories