Recursive function to split a list in python - python

I have a function I've written to take a list of arbitrary values and to split it at certain values into sublists. Basically, I want to take a list and split it at all occurrences of a specific value, returning a list of sublists. I figured the easiest way to do this would be via a recursive function as below.
def recsplit(L, val):
if L.count(val)==0: # If there are no occurrences, return the whole list
return L
elif L.index(val)==0: # If the value is the first element, return everything else
return recsplit(L[1:],val)
else: # Otherwise, split at the first instance of value
return L[:L.index(val)], recsplit(L[L.index(val)+1:],val)
The function should work like this:
>>> P = [1,2,3,4,5,None,None,6,7,8,None,9,10,11,None]
>>> recsplit(P,None)
[[1,2,3,4,5],[6,7,8],[9,10,11]]
Unfortunately I get the following output:
([1, 2, 3, 4, 5, 6, 7], ([8, 9, 10, 11], ([12, 13, 14, 15], [])))
I'm sure there's a way to handle this, but I have tried as many combinations I can think of and none seem to work for me.

I don't think recursion is the easiest way to do this, when you can use itertools.groupby:
from itertools import groupby
lst = [list(g) for k, g in groupby(P, lambda x: x is not None) if k]
print(lst)
# [[1, 2, 3, 4, 5], [6, 7, 8], [9, 10, 11]]
Also keep in mind that recursion is not cheap.

As someone already pointed out, recursive function might not be the best way (at least in python) for this specific task. But since you asked, here is the code with recursive call to generate the exact output you expected.
def recsplit(L, val, out=[[]]):
if L == []:
return out
elif L[0] == val and out[-1] != [] and L[1:].count(val) != len(L[1:]):
return recsplit(L[1:], val, out + [[]])
elif L[0] != val and L[0] is not None:
return recsplit(L[1:], val, out[:-1] + [out[-1] + [L[0]]])
else:
return recsplit(L[1:], val, out)
P = [1,2,3,4,5,None,None,6,7,8,None,9,10,11,None]
P1 = [1,2,None,3,4,5,None,"x","x",None,6,7,8,"x",9,10,11,None,"x","x"]
print("Subs of P by None =", recsplit(P,None))
print("Subs of P1 by x =", recsplit(P1,"x"))
print("Subs of P1 by None =", recsplit(P1,None))
==>
Subs of P by None = [[1, 2, 3, 4, 5], [6, 7, 8], [9, 10, 11]]
Subs of P1 by x = [[1, 2, 3, 4, 5], [6, 7, 8], [9, 10, 11]]
Subs of P1 by None = [[1, 2], [3, 4, 5], ['x', 'x'], [6, 7, 8, 'x', 9, 10, 11], ['x', 'x']]

Related

How can I make selecting highest number more compact with python?

I have this code where I have an array of sizes (i,j) and for each of the j variables, I want to select the highest out of the I variables.
For example, I have array [[2,5,1][12,4,6],[1,7,8],[2,4,5]] and I want to get the highest number out of each of the inner arrays so it should return: [5,12,8,5]
I have the following code which works fine, however, it's a bit messy and hard to read so my question is can I make it more compact?
Here is the code I have:
high_net_profit = list()
for i in range(len(self.accum_profit[0])):
high_value = 0
for j in range((len(self.accum_profit))):
if j == 0:
high_value = self.accum_profit[j][i]
else:
if self.accum_profit[j][i] > high_value: high_value = self.accum_profit[j][i]
high_net_profit.append(high_value)
Try:
lst = [[2, 5, 1], [12, 4, 6], [1, 7, 8], [2, 4, 5]]
out = [max(l) for l in lst]
print(out)
Prints:
[5, 12, 8, 5]
Or:
out = [*map(max, lst)]
print(out)
s = [[2,5,1][12,4,6],[1,7,8],[2,4,5]]
If you just need iterable object:
x = map(max, s)
If you need a list, then wrap into list:
x = list(map(max, s))
Here's a simple solution using numpy. Numpy is a very useful library when working with numbers:
import numpy as np
l_in = [[2, 5, 1], [12, 4, 6], [1, 7, 8], [2, 4, 5]]
# out = [max(l) for l in l_in] # can also be used
out = list(np.max(l_in, axis=1))
print(out)
Output:
[5, 12, 8, 5]

How to deal with this exercise using recursion in Python?

I am struggling to solve an exercise regarding lists in Python. The exercise says:
Write a function that takes a list containing numbers and lists of
numbers inside them, reverses it (including the lists of numbers
inside the main list) using recursion.
Afterwards, the function should return a tuple containing three
elements. The first element represents the amount of all the numbers
present in the list (including the ones in the sublists), the second
element is the sum of all the numbers (also the ones inside the
sublists), and the third element is a sorted list of all the integers.
Again we should use recursion.
I managed to reverse the whole list using two functions, but when it comes to accessing the numbers inside the sublists to use them for the tuple, I really don't know what to do.
Here you can have a look at the list of lists:
lista = [3, 3, 5, [[1, 8, [9, 3]], 3, [2, [9, [5, 6],[9]] ] ]]
Here you can check my code:
def exercise (lista):
lista_ordinata = []
count = 0
somma = 0
reverse_list(lista)
for x,y in enumerate(lista):
if isinstance (x,(int)):
count += 1
else:
count = 0
for num in lista:
if isinstance(num,(int)):
somma += num
for i in lista:
if isinstance(i,int):
lista_ordinata.append(i)
return (count,somma,lista_ordinata)
def is_list (lista):
return isinstance(lista,list)
def reverse_list(lista):
lista_nuova = lista[::-1]
for x,y in enumerate(lista_nuova):
if is_list(y):
lista_nuova[x] = reverse_list(y)
lista.clear()
lista.extend(lista_nuova)
return lista
Here you can see the expected list which I reversed:
lista = [[[[[9], [6, 5], 9], 2], 3, [[3, 9], 8, 1]], 5, 3, 3]
The function must return the following tuple:
(13,66, [1,2,3,5,6,8,9])
The output I get is incorrect:
(4, 11, [5, 3, 3])
The first element should be the counting of all the numbers, and not just the numbers outside the sublists, The sum is also incorrect, The list is not outputting all the numbers.
What should I do? Keep in mind that the "exercise" function should use recursion.
You are using recursion just fine in the reverse function, just use recursion in you exercise function:
def exercise(lista):
lista_ordinata = []
count = 0
somma = 0
# reverse_list(lista)
for x in lista:
if isinstance(x, list):
recursion = exercise(x) # call recursion until you have a list with only integers
# and add the result to your running totals
count += recursion[0]
somma += recursion[1]
lista_ordinata.extend(recursion[2])
else:
count += 1
somma += x
lista_ordinata.append(x)
lista_ordinata = sorted(list(set(lista_ordinata)))
return count, somma, lista_ordinata
print(exercise(lista))
(13, 66, [1, 2, 3, 5, 6, 8, 9])
def exercise(a, polish=sorted):
if not isinstance(a, list):
return 1, a, {a}
a.reverse()
amount, sum, numbers = 0, 0, set()
for b in a:
a, s, n = exercise(b, set)
amount += a
sum += s
numbers |= n
return amount, sum, polish(numbers)
Or with a little helper doing the reversals and collecting the numbers:
def exercise(a):
def go(a):
if isinstance(a, list):
a.reverse()
for b in a:
go(b)
else:
numbers.append(a)
numbers = []
go(a)
return len(numbers), sum(numbers), sorted(set(numbers))
Demo:
lista = [3, 3, 5, [[1, 8, [9, 3]], 3, [2, [9, [5, 6],[9]] ] ]]
>>> exercise(lista)
(13, 66, [1, 2, 3, 5, 6, 8, 9])
>>> lista
[[[[[9], [6, 5], 9], 2], 3, [[3, 9], 8, 1]], 5, 3, 3]
Here's a way without any loops at all.
First of all, we can have a definition for a partial insertion sort, which inserts any value to its correct position in an existing sorted array.
def partialInsertionSort(val, idx, sortarr):
if idx == len(sortarr):
sortarr.append(val)
elif val < sortarr[idx]:
sortarr.insert(idx, val)
elif val > sortarr[idx]:
sortarr = partialInsertionSort(val, idx+1, sortarr)
return sortarr
As you travel through any list a, unless you've reached the end of a, there are two possibilities:
The current item is another list -> Recurse through the sublist!
The current item is a digit -> Increment total and count. If this digit is a previously unseen digit, 'insert' it to your list of sorted digits.
Done with the above two possible options, you have thus processed the current element, and can move to the next element by incrementing the index i.
def reverse_and_sort(a, i, out, sortarr, total, count):
if i==len(a):
return out, sortarr, total, count
if isinstance(a[i], list):
a[i], sortarr, total, count = reverse_and_sort(a[i], 0, [], sortarr, total, count)
else:
total += a[i]
count += 1
if a[i] not in sortarr:
sortarr = partialInsertionSort(a[i], 0, sortarr)
out.insert(0, a[i])
return reverse_and_sort(a, i+1, out, sortarr, total, count)
Test:
rev, srt, total, count = reverse_and_sort(lista, 0, [], [], 0, 0)
print(rev) #[[[[[9], [6, 5], 9], 2], 3, [[3, 9], 8, 1]], 5, 3, 3]
print(srt) #[1, 2, 3, 5, 6, 8, 9]
print(total) #66
print(count) #13
A relatively short version, which uses the add operator is given by that:
from operator import add
def sorter(l):
if isinstance(l, list):
temp = [0, 0, []]
for e in map(sorter, l):
temp = list(map(add, e, temp))
return temp[0], temp[1], sorted(set(temp[2]))
return 1, l, [l]
It should give the correct result:
test_list = [[[[[9], [6, 5], 9], 2], 3, [[3, 9], 8, 1]], 5, 3, 3]
sorter(test_list)
>>> (13, 66, [1, 2, 3, 5, 6, 8, 9])
Two liner approach!
Here is a simple solution to the problem, using indirect recursion. Indirect recursion is where a function f(x) calls a function g(x) which in turn calls f(x).
If your problem was just the first part, the solution is just a simple 2 liner -
f = lambda x: [g(i) for i in reversed(x)]
g = lambda x: f(x) if type(x)==list else x
f(lista)
[[[[[9], [6, 5], 9], 2], 3, [[3, 9], 8, 1]], 5, 3, 3]
Beautiful right?
The main idea here is that at an element level you just need to check if the element is an integer and return it, else you need to use another function that lets you iterate over the list. You can have counters at the level of the second function which lets you track the sum and the count and return it with the final function call.
Since you also have the 2nd part to the solution which is using a few counters, you can modify the above code for g(x) to incorporate that as below -
cnt = []
sm = []
#function to iterate over a reversed list
f = lambda x: [g(i) for i in reversed(x)]
#function to call f if list else updated counters and return element
def g(x):
if type(x)==list:
return f(x)
else:
cnt.append(1) #ONLY MODIFICATION
sm.append(x) #ONLY MODIFICATION
return x
#call f and return sum of counters
def rec(l):
o = f(l)
return o, (sum(cnt), sum(sm), sorted(sm))
out, tup = rec(lista)
print(out)
print(tup)
[[[[[9], [6, 5], 9], 2], 3, [[3, 9], 8, 1]], 5, 3, 3]
(13, 66, [1, 2, 3, 3, 3, 3, 5, 5, 6, 8, 9, 9, 9])
def exercise(lista, total = 0, count = 0, all_int = []):
for item in lista:
if isinstance(item, list):
count, total, all_int = exercise(item, total, count, all_int)
elif isinstance(item, int):
total += item
count += 1
if item not in all_int:
all_int.append(item)
all_int.sort()
lista.reverse()
return count, total, all_int
Maybe this would help :)
def exercise (lista):
reverse_list(lista)
r = count_numbers(lista), sum_numbers(lista), sorted(pull_out(lista))
return r
def is_list (lista):
return isinstance(lista,list)
def reverse_list(lista):
lista_nuova = []
for e in lista[::-1]:
if is_list(e):
lista_nuova.append(reverse_list(e))
else:
lista_nuova.append(e)
return lista_nuova
def count_numbers(lista):
c = 0
for e in lista:
if is_list(e):
c += count_numbers(e)
else:
c+=1
return c
def sum_numbers(lista):
s = 0
for e in lista:
if is_list(e):
s += sum_numbers(e)
else:
s+=e
return s
def pull_out(lista):
b_lista = []
for e in lista:
if is_list(e):
b_lista.extend(pull_out(e))
else:
b_lista.append(e)
return b_lista
lista = [3, 3, 5, [[1, 8, [9, 3]], 3, [2, [9, [5, 6],[9]] ] ]]
r = exercise(lista)
print(r)
# (13, 66, [1, 2, 3, 3, 3, 3, 5, 5, 6, 8, 9, 9, 9])
I did it with separate functions for clarity.

Apply a function to every item of an arbitrarily nested list

I have a nested list a = [1, 2, [3, 4], 5] and I want to apply a function that will raise every number to the power of 2. The result will be like this:
a = [1, 4, [9, 16], 25]
I tried a = [list(map(lambda x : x * x, x)) for x in a] but it gives this error
'int' object is not iterable
How we can fix this issue? How can I apply a function over a nested list?
You probably need a recursive function that distinguishes between lists and scalars:
def square(item):
if isinstance(item, list):
return [square(x) for x in item]
else:
return item * item
square(a)
#[1, 4, [9, 16], 25]
Incidentally, this approach works for arbitrary-nested lists.
Here's a more general solution:
def apply(item, fun):
if isinstance(item, list):
return [apply(x, fun) for x in item]
else:
return fun(item)
apply(a, lambda x: x * x)
#[1, 4, [9, 16], 25]
You are decomposing your list into its elements -some of them are lists which can not be multiplied with itself ([3,4]*[3,4]).
Not sure if that is smart - but you can use a "recursing" lambda:
a =[1, 2, [3, 4], 5]
l = lambda x : x * x if isinstance(x,int) else list(map(l,x))
a = list(map(l, a))
print(a)
Output:
[1, 4, [9, 16], 25]
Works also for "deeper" levels:
a =[1, 2, [3, [7,8], 4], 5]
Output:
[1, 4, [9, [49, 64], 16], 25]
but will crash if you mix non-iterables things into the mix

How can i create multidimensional list in Python?

How can i create a multidimensional list while iterating through a 1d list based upon some condition.
I am iterating over a 1d list and whenever i find a '\n' i should append the so created list with a new list, for example,
a = [1,2,3,4,5,'\n',6,7,8,9,0,'\n',3,45,6,7,2]
so I want it to be as,
new_list = [[1,2,3,4],[6,7,8,9,0],[3,45,6,7,2]]
how should i do it? Please help
def storeData(k):
global dataList
dlist = []
for y in k:
if y != '\n':
dlist.append(y)
else:
break
return dlist
This is what i have tried.
example code:
lst = [[]]
for x in a:
if x != '\n':
lst[-1].append(x)
else:
lst.append([])
print(lst)
output:
[[1, 2, 3, 4, 5], [6, 7, 8, 9, 0], [3, 45, 6, 7, 2]]
Using itertools.groupby would do the job (grouping by not being a linefeed):
import itertools
a = [1,2,3,4,5,'\n',6,7,8,9,0,'\n',3,45,6,7,2]
new_list = [list(x) for k,x in itertools.groupby(a,key=lambda x : x!='\n') if k]
print(new_list)
We compare the key truth value to filter out the occurrences of \n
result:
[[1, 2, 3, 4, 5], [6, 7, 8, 9, 0], [3, 45, 6, 7, 2]]
This is how I did it but there must be better solutions.
x = 0
output = [[]]
for item in a:
if item != "\n":
output[x].append(item)
else:
x += 1
output.append([])
print(output)
Here is the basic approach:
EDIT: Good heavens! Silly bug... here's a fix:
>>> sub = []
>>> final = []
>>> for e in a:
... if e == '\n':
... final.append(sub)
... sub = []
... else:
... sub.append(e)
... else:
... final.append(sub)
...
>>> final
[[1, 2, 3, 4, 5], [6, 7, 8, 9, 0], [3, 45, 6, 7, 2]]
>>>
There are other ways, but this is the naive imperative way.

List of Lists as result in list comprehension

I am trying to find a solution for a problem where I have to loop in a list for every element.
This is not the real problem that I am trying to solve, but I am using a simple example to illustrate the problem and what I want to understand.
aList= [3, 4, 5, 6, 8, 9, 10,12]
I should regroup number divisible by each others.
result should give:
result = [[3], [4], [5], [6,3], [4,8],[3,9], [5,10], [3,4,6,12]]
I use this function:
def divisible(x,y):
if x%y== 0:
return True
else:
return False
well, to solve this problem using two loops, we can use:
globaList= []
for x in aList:
internalist=[]
internalist.append(x)
for y in aList:
if divisible(x,y):
internalist.append(y)
globaList.append(internalist)
I tried to write this double-loops in list comprehension, but didn't know how to make better way.
result= [[x for x in aList ] for y in aList if divisible(x,y) ]
def divisible(x,y):
if x%y== 0:
return True
else:
return False
aList= [3, 4, 5, 6, 8, 9, 10,12]
>>> [[x for x in aList if divisible(y,x)] for y in aList]
[[3], [4], [5], [3, 6], [4, 8], [3, 9], [5, 10], [3, 4, 6, 12]]
You don't really need a helper function divisible:
aList= [3, 4, 5, 6, 8, 9, 10,12]
print [[x for x in aList if y % x == 0] for y in aList]
If you really want to use a helper function you can make that more succinct by going:
def divisible(x,y):
return x % y == 0

Categories