sum of nested list in Python - python

I try to sum a list of nested elements
e.g, numbers=[1,3,5,6,[7,8]] should produce sum=30
I wrote the following code :
def nested_sum(L):
sum=0
for i in range(len(L)):
if (len(L[i])>1):
sum=sum+nested_sum(L[i])
else:
sum=sum+L[i]
return sum
The above code gives following error:
object of type 'int' has no len()
I also tried len([L[i]]), still not working.
Anyone can help? It is Python 3.3

You need to use isinstance to check whether an element is a list or not. Also, you might want to iterate over the actual list, to make things simpler.
def nested_sum(L):
total = 0 # don't use `sum` as a variable name
for i in L:
if isinstance(i, list): # checks if `i` is a list
total += nested_sum(i)
else:
total += i
return total

One alternative solution with list comprehension:
>>> sum( sum(x) if isinstance(x, list) else x for x in L )
30
Edit:
And for lists with more than two levels(thx #Volatility):
def nested_sum(L):
return sum( nested_sum(x) if isinstance(x, list) else x for x in L )

It is generally considered more pythonic to duck type, rather than explicit type checking. Something like this will take any iterable, not just lists:
def nested_sum(a) :
total = 0
for item in a :
try:
total += item
except TypeError:
total += nested_sum(item)
return total

I would sum the flattened list:
def flatten(L):
'''Flattens nested lists or tuples with non-string items'''
for item in L:
try:
for i in flatten(item):
yield i
except TypeError:
yield item
>>> sum(flatten([1,3,5,6,[7,8]]))
30

A quick recursion that uses a lambda to handle the nested lists:
rec = lambda x: sum(map(rec, x)) if isinstance(x, list) else x
rec, applied on a list, will return the sum (recursively), on a value, return the value.
result = rec(a)

This code also works.
def add_all(t):
total = 0
for i in t:
if type(i) == list: # check whether i is list or not
total = total + add_all(i)
else:
total += i
return total

An example using filter and map and recursion:
def islist(x):
return isinstance(x, list)
def notlist(x):
return not isinstance(x, list)
def nested_sum(seq):
return sum(filter(notlist, seq)) + map(nested_sum, filter(islist, seq))
And here is an example using reduce and recursion
from functools import reduce
def nested_sum(seq):
return reduce(lambda a,b: a+(nested_sum(b) if isinstance(b, list) else b), seq)
An example using plain old recursion:
def nested_sum(seq):
if isinstance(seq[0], list):
head = nested_sum(seq[0])
else:
head = seq[0]
return head + nested_sum(seq[1:])
An example using simulated recursion:
def nested_sum(seq):
stack = []
stack.append(seq)
result = 0
while stack:
item = stack.pop()
if isinstance(item, list):
for e in item:
stack.append(e)
else:
result += item
return result
Adjustment for handling self-referential lists is left as an exercise for the reader.

def sum_nest_lst(lst):
t=0
for l in lst:
if(type(l)==int):
t=t+l
if(type(l)==list):
t=t+sum(l)
print(t)

def nnl(nl): # non nested list function
nn = []
for x in nl:
if type(x) == type(5):
nn.append(x)
if type(x) == type([]):
n = nnl(x)
for y in n:
nn.append(y)
return sum(nn)
print(nnl([[9, 4, 5], [3, 8,[5]], 6])) # output:[9,4,5,3,8,5,6]
a = sum(nnl([[9, 4, 5], [3, 8,[5]], 6]))
print (a) # output: 40

A simple solution would be to use nested loops.
def nested_sum(t):
sum=0
for i in t:
if isinstance(i, list):
for j in i:
sum +=j
else:
sum += i
return sum

L = [1, 2, 3, [4, 5, 6], 5, [7, 8, 9]]
total = 0 # assign any var
for a in L: # assign index and start to iterate using if else
if (isinstance(a, list)): # since its a list you are basically repeating the prev step
for b in a:
total += b
else:
total += a
print(total)

def list_sum(L):
return sum(list_sum(x) if isinstance(x, list) else x for x in L)

def nested_sum(lists):
total = 0
for lst in lists:
s = sum(lst)
total += s
return total

#nested sum
l = [[1, 2], [3,5], [6,2], [4, 5, 6,9]]
def nested_sum(lst):
sum = 0
for i in lst:
for j in i:
sum = sum + j
print(sum)
nested_sum(l)

Related

How to replace a numerical value in a Python list

So I got this list of lists:
lst = [[0,1],2,[3]]
and I got a list of tuples:
lst_2 = [("x1","y1"),("x2","y2"), ("x3","y3"), ("x4","y4")]
I want to replace values inside lst with the index 0 value of each of the tuples in lst_2, and the tuple taken depends on the numerical value in lst. So it becomes:
lst = [["x1","x2"], "x3", ["x4"]]
Please don't roast me thanks so much
Try this:
lst = [[0,1],2,[3]]
lst_2 = [("x1","y1"),("x2","y2"), ("x3","y3"), ("x4","y4")]
res = []
for l in lst:
if isinstance(l, list):
res += [[lst_2[i][0] for i in l]]
else:
res += [lst_2[l][0]]
print(res)
Or with List Comprehensions:
res = [[lst_2[i][0] for i in l] if isinstance(l, list) else lst_2[l][0] for l in lst]
[['x1', 'x2'], 'x3', ['x4']]
You could use recursion to allow lst to have deeper levels of nesting:
def produce(template, data):
return [
produce(nested, data) for nested in template
] if isinstance(template, list) else data[template][0]
# Example
lst = [[0,[1]],2,[3]]
lst_2 = [("x1","y1"),("x2","y2"), ("x3","y3"), ("x4","y4")]
result = produce(lst, lst_2)

Python list into sublists based on the equality of first digit of the list elements

a = [58,23,12,24,11,63,54]
I want to make the above list as follows:
[[58,54],[23,24],[12,11]]
i.e, I want to make the list of sublists whose elements start with same digit and having count > 1.
You can use dictionary
from collections import defaultdict
a = [58,23,12,24,11,63,54]
d = defaultdict(list)
for num in a:
d[str(num)[0]].append(num)
res = [v for v in d.values() if len(v) > 1]
print(res)
Output:
[[58, 54], [23, 24], [12, 11]]
This code groups integer of any digit on the basis of the first digit. I hope this is what you are looking for. Also this may not be the most efficient way of doing this.
a=[58,23,12,24,11,63,54]
final=[[],[],[],[],[],[],[],[],[],[]]
for i in a:
count=0
a=i
while (i>0):
count+=1
i=i//10
final[int(a/(10**(count-1)))].append(a)
a=[]
for i in final:
if len(i)>1:
a.append(i)
print(a)
you can use itertools.groupby to group a list by any key function you want:
note the use of Python 3.8's "walrus operator"
from itertools import groupby
a=[58,23,12,24,11,63,54]
get_first_digit = lambda n: str(n)[0]
sorted_by_digit = sorted(a, key=get_first_digit, reverse=True) # sorting is a must for groupby
result = [y for _,g in groupby(sorted_by_digit, key=get_first_digit) if len(y:=list(g))>1]
print(result)
Output:
[[58, 54], [23, 24], [12, 11]]
I have an answer to this, although it is very big.
a = [58,23,12,24,11,63,54]
s = [[a[0]]]
def firstdigit(n):
while n>=10:
n = n//10
return n
def arrange(s, i):
for j in range(len(s)):
if firstdigit(s[j][0]) == firstdigit(i):
s[j].append(i)
break
else:
m=[]
m.append(i)
s.append(m)
return s
def check(s):
for i in s:
if len(i) <= 1:
s.remove(i)
return s
for i in a[1:]:
s = arrange(s, i)
#checks for count>1
s = check(s)
print(s)
This code contains only basics with no modules imported.

How to find the number of lists in an input? (python)

def lists(A: list) -> int:
'''Return the total number of lists in A (including A itself).
Each element of A and any nested lists are either ints or other lists.
Example:
>>> lists([1, 2, 3])
1
>>> lists([[1], [2], [3]])
4
>>> lists([[[1, 2], [], 3]])
4
'''
Does anyone know how to do this?
All I have is
for i in range(0, len(A)):
if (isinstance(A[i], list)):
count=count+1
return(lists(A[i]))
else:
B=A[i:]
return(count)
Here is a 'dirty' but easy way to do it
def lists(l):
'''
Return the total number of lists in A (including A itself).
Each element of A and any nested lists are either ints or other lists.
'''
# convert the list to string and count the ['s
# each [ is the start of a list, so the number of ['s equals
# the number of lists
nr_of_lists = str(l).count('[')
# return the number of sublists
return nr_of_lists
No recursion required
Here's one way to write it:
def numlists(lst, num = 1):
for item in lst:
if isinstance(item, list):
num += numlists(item)
return num
Sample outputs:
print(numlists([1, 2, 3])) # 1
print(numlists([[1], [2], [3]])) # 4
print(numlists([[[1, 2], [], 3]])) # 4
print(numlists([[1,[2,3,[4,5,[6]]]],7,8,[9]])) # 6
You should do this with recursion:
def count_list(a):
result = 0
if isinstance(a, list):
result += 1
try:
for b in a:
result += count_list(b)
except:
pass
return result
def lists(a):
if not isinstance(a, list):
return 0
s = 1
for x in a:
s += lists(x)
return s
print lists([])
print lists([1,2,3])
print lists([[1], [2], [3]])
print lists([[[1, 2], [], 3]])
def lists(A):
return 1 + sum(lists(e) if isinstance(e, list) else 0 for e in A)

How to normalize a list such that it works for all cases

Task: Given this code:
def normalize(lst):
s = sum(lst)
return list(map(lambda v: v / s, lst))
This code works for:
normalize([1, 2, 5, 4])
But not for this (due to division of zero):
normalize([1, 2, -7, 4])
Without modifying the normalize function, implement this strategy in the safe_normalize function such that:
safe_normalize([1, 2, -5, 2])
gives
[1, 2, -5, 2]
I tried this but it fails:
def safe_normalize(lst):
def normalize(lst):
s = sum(lst)
if s == o:
return s
elif s!= 0:
new_s = s
return list(map(lambda v: v / new_s, lst))
return normalize(lst)
If you can't modify normalize but you assume it is given you can just wrap call to it in try except block and return input list if ZeroDivisioneError occurs.
def safe_normalize(lst):
try:
return normalize(lst)
except ZeroDivisionError:
return lst
How about the good old or-idiom:
def safe_normalize(lst):
s = sum(lst) or 1
return list(v/s for v in lst)
Off-topic, but note how are generator-expressions more readable. List comprehension is even better:
[v/s for v in lst]
You can also add a flag to enforce safeness and catch the exception.
Also, in python 2.7 you will get integer division. If you don't want this then add a float conversion. Also, in 2.7 the call to list() is extraneous as map() returns a list anyway. You might want to add the list call in the client code.
def normalize(lst, safe=False):
s = sum(lst);
try:
return map(lambda v: float(v)/s, lst)
except ZeroDivisionError:
if safe:
return lst
else:
raise
print(normalize([1,2,3,4]))
print(normalize([1,2,3,-6], safe=True))
print(list(normalize([1,2,3,-6], safe=True)))
for item in normalize([1,2,3,-6], safe=True):
print(item)

How to join array based on position and datatype in Python?

I have a few arrays containing integer and strings. For example:
myarray1 = [1,2,3,"ab","cd",4]
myarray2 = [1,"a",2,3,"bc","cd","e",4]
I'm trying to combine only the strings in an array that are next to each other. So I want the result to be:
newarray1= [1,2,3,"abcd",4]
newarray2= [1,"a",2,3,"bccde",4]
Does anyone know how to do this? Thank you!
The groupby breaks the list up into runs of strings and runs of integers. The ternary operation joins the groups of strings and puts them into a temporary sequence. The chain re-joins the strings and the runs of integers.
from itertools import groupby, chain
def joinstrings(iterable):
return list(chain.from_iterable(
(''.join(group),) if key else group
for key, group in
groupby(iterable, key=lambda elem: isinstance(elem, basestring))))
>>> myarray1 = [1,2,3,"ab","cd",4]
>>> newarray1 = [myarray1[0]]
>>> for item in myarray1[1:]:
... if isinstance(item, str) and isinstance(newarray1[-1], str):
... newarray1[-1] = newarray1[-1] + item
... else:
... newarray1.append(item)
>>> newarray1
[1, 2, 3, 'abcd', 4]
reduce(lambda x, (tp, it): tp and x + ["".join(it)] or x+list(it), itertools.groupby( myarray1, lambda x: isinstance(x, basestring) ), [])
a = [1,2,3,"ab","cd",4]
b = [1,a,2,3,"bc","cd","e",4]
def func(a):
ret = []
s = ""
for x in a:
if isinstance(x, basestring):
s = s + x
else:
if s:
ret.append(s)
s = ""
ret.append(x)
return ret
print func(a)
print func(b)

Categories