Related
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]]
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]]
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:
I am trying to expand sublists within a list. I wrote it for one layer of sublists within the list. Is there a better way to code this so to scale for arbitrary number of nested lists? See example as follows:
a = [1, 2, 3]
b = [4, 5, 6]
c = [a, b]
def expand(x):
# expands a list of lists with no further sublists
a = []
for i in x:
# could also here do a += i and avoid the second for loop
for j in i:
a.append(j)
return a
print expand(c)
Returns the desired [1, 2, 3, 4, 5, 6]
To further clarify, I am wondering how to better scale for
`e = [c, a, b]' and further nested iterations.
c = [[1, 2, 3], [4, 5, 6]]
# first method
res = [x for lst in c for x in lst]
# second method
from itertools import chain
res = list(chain(*c))
Edit: he wants iteratively nested lists!
c = [[[1], [2, 3]], [4, 5], [[6, 7, 8], 9]]
def expand(lst):
try:
for a in lst:
for b in expand(a):
yield b
except TypeError:
yield lst
res = list(expand(c)) # => [1, 2, 3, 4, 5, 6, 7, 8, 9]
Use Recursion:
def flat_list(l):
for a in l:
if isinstance(a, list):
for x in flat_list(a):
yield x
else:
yield a
a = [1, 2, 3]
b = [4, 5, 6]
c = [a, b]
e = [c, a, b]
print list(flat_list(c))
print list(flat_list(e))
the output is:
[1, 2, 3, 4, 5, 6]
[1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6]
One solution:
def flatten(ls):
result = []
stack = [ls]
while stack:
if isinstance(stack[-1], list):
try: stack.append(stack[-1].pop(0))
except IndexError: stack.pop() # remove now-empty sublist
else:
result.append(stack.pop())
return result
a = [1, 2, 3]
b = [4, 5, 6]
c = [a, b]
e = [c, a, b]
print flatten(e)
Output:
[1, 2, 3, 4, 5, 6]
Or :
Using sum:
>>> e
[[[1, 2, 3], [4, 5, 6]], [1, 2, 3], [4, 5, 6]]
>>> sum(e[0], [])
[1, 2, 3, 4, 5, 6]
Using reduce:
>>> reduce(lambda x,y: x+y, e[0])
[1, 2, 3, 4, 5, 6]
>>>
I am looking for the Josephus_problem ,but the result is not my Expected. Why?
def J(n,x):
li=range(1,n+1)
k=0
res=[]
while len(li)>1:
k= (x+k-1) % len(li)
li.pop(k)
res.append(li)
#print li
return res
print J(5,3)
Expected Output:
[1, 2, 4, 5]
[2, 4, 5]
[2, 4]
[4]
Actual Output:
[[4], [4], [4], [4]]
You need to append copy of list here:
res.append(li[:]) # <-- not res.append(li) !!!
The actual reason of what's going on it that list is mutable data structure in Python. Look at this snippet
>>> l = [1,2,3]
>>> p = [l,l,l]
>>> p
[[1, 2, 3], [1, 2, 3], [1, 2, 3]]
>>> l.pop()
3
>>> p
[[1, 2], [1, 2], [1, 2]]