function that sums up nested list values? - python

So im attempting to create a recursion function where it takes each item in the list and sums it up altogether, now I know theres a simple built in function sum(a) but I'm trying to work with nested lists such as this below but I keep getting thrown an error.
def sumList():
list2 = [1, [2, 3,[4, 5, 6], 7, [8, [9, 10]], 11]]
newlist = []
lol = 0
for i in range (len(list2)):
if type(list2[i]) == type([]):
print list2[i], "here"
for i in range (len(list2[i])):
lol += (len(list2[i]))
newlist.append(i[:len(i)+1])
if len(list2)==0:
return None
else:
print list2[i]
lol+=list2[i]
print lol
sumList()
Now I know i've got a lot implemented in the program that I imagine isn't needed, but the error I
keep getting is
1
[2, 3, [4, 5, 6], 7, [8, [9, 10]], 11] here
TypeError: object of type 'int' has no len()

In general, you could flatten your list of lists and search for min in the flattened list. There are many recipes for flattening. Below is one that I took from here.
import collections
def flatten(iterable):
for el in iterable:
if isinstance(el, collections.Iterable) and not isinstance(el, str):
yield from flatten(el)
else:
yield el
list2 = [2, 3, [4, 5, 6], 7, [8, [9, 10]], 11]
print(list(flatten(list2)))
# [2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
print(sum(flatten(list2)))
# 65

def r_sum(mylist,nsum=0):
for i in mylist:
if isinstance(i,int):
nsum += i
else:
nsum += r_sum(i)
return nsum

# Python 2.7
def recursiveSum(data):
# This naively assumes that if it's not an int, it's a list
# You may want to add more error handling if you expect dirtier data
if isinstance(data, int): return data
mySum = 0
for i in data: mySum += recursiveSum(i)
return mySum
list2 = [1, [2, 3,[4, 5, 6], 7, [8, [9, 10]], 11]]
print recursiveSum(list2) # Should get 66

Related

How to zip several undefined different lenght lists in python?

I am trying to write a function that get a list of lists with different length as input and return the zipped result.
What I am looking for is that to expand my code below to any amount of lists. (I cannot use Zip Longest function since I am trying to do this on our system that does not have most of python functions including
zip longest function)
Here is my code:
a = [[1,2,3,4],[5,6],[7,8,9]]
def myzip(a):
temp1=[]
temp2=[]
temp3=[]
lens=[]
t=1
for i in a:
if(t==1):
temp1=i
lens.append(len(temp1))
t+=1
elif(t==2):
temp2=i
lens.append(len(temp2))
t+=1
elif(t==3):
temp3=i
lens.append(len(temp3))
for i in range(max(lens)):
if(i<len(temp1)):
print(temp1[i])
if(i<len(temp2)):
print(temp2[i])
if(i<len(temp3)):
print(temp3[i])
myzip(a)
Output:
1
5
7
2
6
8
3
9
4
This function works only for 3 lists because I am using Temp lists in order to achieve the zipped result But I want to make this code works for any number of lists. for example I able to run for [[1,2,3,4],[5,6],[7,8,9],[11,33]] or [[1,2,3,4],[5,6]] or [[1,2,3,4],[5,6],[7,8,9],...,[25,22]]
How about this:
from itertools import zip_longest
lists = [[1, 2, 3, 4], [5, 6], [7, 8, 9], [11, 33]]
for item in [x for t in zip_longest(*lists) for x in t]:
if item is not None:
print(item)
Output:
1
5
7
11
2
6
8
33
3
9
4
Or to just get them as a list:
items = [x for t in zip_longest(*lists) for x in t if x is not None]
Note: #MarkM made a worthwhile remark - if your source data contains None, this approach will have a problem in that it will filter them out. You should tell zip_longest to use a different fillvalue in that case, that does not show up in your data. For example:
items = [x for t in zip_longest(*lists, fillvalue='') for x in t if x is not None]
If you cannot import itertools for very specific reasons (as mentioned in the comments), you could just use the implementation shown in the documentation (https://docs.python.org/3/library/itertools.html#itertools.zip_longest):
def repeat(object, times=None):
if times is None:
while True:
yield object
else:
for i in range(times):
yield object
def zip_longest(*args, fillvalue=None):
iterators = [iter(it) for it in args]
num_active = len(iterators)
if not num_active:
return
while True:
values = []
for i, it in enumerate(iterators):
try:
value = next(it)
except StopIteration:
num_active -= 1
if not num_active:
return
iterators[i] = repeat(fillvalue)
value = fillvalue
values.append(value)
yield tuple(values)
Something like this could also work for you:
def flatten_list(my_list):
flat_list = []
for element in my_list:
if type(element) is list: #if element is a list, iterate through it
for item in element:
flat_list.append(item)
else:
flat_list.append(element)
return flat_list
nested_list = [[1, 2, 3, 4], [5, 6, 7], [8, 9, 10],[1,2,3],[1,2,4],[4,6,7,8]]
print('Original List', nested_list)
print('Flat List', flatten_list(nested_list))
Output
Original List [[1, 2, 3, 4], [5, 6, 7], [8, 9, 10], [1, 2, 3], [1, 2, 4], [4, 6, 7, 8]]
Flat List [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 1, 2, 4, 4, 6, 7, 8]

Python retrieve a nested list of values between a tag in a list

I have a list that is:
[2,3,5,"TAG",6,7,3,2,6,"TAG",9,9,8,3]
I want to return a list containing nested sublists of values that are in between the "TAG"s.
So the resulting list would look like:
[[2,3,5], [6,7,3,2,6], [9,9,8,3]]
I created a method below that attempts to accomplish this but does not work:
def returnListBetweenTag(lst, tag):
temp = []
k = lst.index(tag)
while lst != []:
temp.append(lst[0:k])
del lst[0:k+1]
return temp
This prints out:
[[2, 3, 5], [6, 7, 3], [6, 'TAG', 9], [8, 3]]
Can anyone please help me understand what I am doing wrong, and some suggestions to fix it?
This is reasonably straight forward when using a generator like:
Code:
def divide_list(a_list, divider):
sub_list = []
for item in a_list:
if item == divider:
yield sub_list
sub_list = []
else:
sub_list.append(item)
yield sub_list
Test Code:
data = [2,3,5,"TAG",6,7,3,2,6,"TAG",9,9,8,3]
print(list(divide_list(data, "TAG")))
Results:
[[2, 3, 5], [6, 7, 3, 2, 6], [9, 9, 8, 3]]
Here's a way to accomplish this with itertools.groupby.
from itertools import groupby
ls = []
for key, group in groupby(lst, lambda x: x != "TAG"):
if key:
ls.append(list(group))
print ls
# [[2, 3, 5], [6, 7, 3, 2, 6], [9, 9, 8, 3]]
This can also be implemented as a comprehension:
ls = [list(group) for key, group in groupby(lst, lambda x: x != "TAG") if key]
This is really not a good way to do it, but you can make it work.
First, you never do the k = lst.index(tag) again after the first time.
So, with your list, k turns out to be 3 forever. But you keep taking the first three elements off the list, so first k[0:3] is [2, 3, 5], then it's [6, 7, 3], then it's 3, [6, 'TAG', 9], and so on. You need to keep calling it each time.
Second, what happens when there are no more 'TAG's left in the list? That lst.index will raise an exception. So you need to handle that.
def returnListBetweenTag(lst, tag):
temp = []
while lst != []:
try:
k = lst.index(tag)
except ValueError:
temp.append(lst)
break
temp.append(lst[0:k])
del lst[0:k+1]
return temp
Logic is just tracking the 'TAG' keyword and when it found just empty the temp list and start adding from index 0 again.
This will work for you:
data=[2,3,5,"TAG",6,7,3,2,6,"TAG",9,9,8,3]
def values(datae):
final_values=[]
store=[]
for i in data:
if i!='TAG':
store.append(i)
else:
final_values.append(store)
store=[]
final_values.append(store)
return final_values
print(values(data))
output:
[[2, 3, 5], [6, 7, 3, 2, 6], [9, 9, 8, 3]]

Finding a minimum value in a list of lists and returning that list

This code is supposed to iterate over the list of lists and return the entire list that contains the smallest value. I have already identified that it keeps returning the list at index[0], but I cannot figure out why. Any help, or even hints, would be greatly appreciated.
def list_with_min(list_of_lists):
m = 0
for i in range(len(list_of_lists)-1):
list_i = list_of_lists[m]
min_list = min(list_of_lists[m])
if min_list < list_i[0]:
m = i
answer = list_of_lists[m]
return answer
print(list_with_min([[9, 10, 15], [1, 8, 4], [-3, 7, 8]]))
# [9, 10, 15]--------> should be [-3, 7, 8]
print(list_with_min([[5], [9], [6], [2], [7], [10], [72]]))
# [5]----------------> should be [2]
print(list_with_min([[-2, 6, 9], [-9, 6, 9], [4, 8, 2], [5, -2]]))
# [-2, 6, 9]---------> should be [[-2, 6, 9], [5, -2]] (I assume two lists with the same minimum value should both be returned?)
You can provide a key to the function min, that is a function used for comparison. It turns out that here you want key to be the function min itself.
list_of_lists = [[9, 10, 15], [1, 8, 4], [-3, 7, 8]]
min(list_of_lists, key=min) # [-3, 7, 8]
This does not return multiple minima, but can be improved to do so.
list_of_lists = [[9, 10, 15], [1, -3, 4], [-3, 7, 8]]
min_value = min(map(min, list_of_lists))
[lst for lst in list_of_lists if min(lst) == min_value] # [[1, -3, 4], [-3, 7, 8]]
you can get this in one line with a list comprehension (ive added three though to help you work through the logic), it deals with duplicates differently however:
#for each list return a list with the minimum and the list
mins_and_lists = [[min(_list), _list] for _list in lists]
#find the minimum one
min_and_list = min([[min(_list), _list] for _list in lists])
#parse the result of the minimum one list
minimum, min_list = min([[min(_list), _list] for _list in lists])
if you want to handle duplicate minimums by returning both then:
dup_mins = [_list for _min, _list in mins_and_lists if _min == minimum]
EDIT: A more python way could be this:
def list_with_min(l):
min_vals = [min(x) for x in l]
return l[min_vals.index(min(min_vals))]
A bit bulky but it works...
l=[[9, 10, 15], [1, 8, 4], [-3, 7, 8]]
def list_with_min(l):
m = min(l[0])
for i in l[1:]:
m = min(i) if min(i) < m else m
for i in l:
if m in i:
return i
print(list_with_min(l))
Output:
[-3, 7, 8]
You could also use this:
min([ (min(a),a) for a in list_of_lists ])[1]
Your condition just makes no sense. You're checking
if min_list < list_i[0]:
which means, if the smallest value of list_i is less than the first value of list_i.
I don't think you'd ever want to compare to just list_i[0]. You need to store min_list across loops, and compare to that.

Absolute value from nested list using recursion

I have this nested list:
list1 = [2,-6, [8,-12,-12, [4, [-6], -3]], 7, [3.55, -3.55]].
And I have to use recursion to get absolute values of all elements from the lists so output stays as a list:
[2, 6, [8, 12, 12, [4, [6], 3]], 7, [3.55, 3.55]].
This is the code:
def rec_abs(a):
new_list=[]
for el in a:
if isinstance(el, list):
new_list.append(rek_abs(el))
else:
new_list.append(el)
return new_list
print(rek_abs([2,-6, [8,-12,-12, [4, [-6], -3]], 7, [3.55, -3.55]]))
Can You give me some tips to solve it (I dont expect full solultion, just some tips)?
Thank You a lot!
Everything is perfect, just use abs() to convert it into absolute value
Note : You have typo in code as rec_abs and rek_abs, which I modified it in below code
def rec_abs(a):
new_list=[]
for el in a:
if isinstance(el, list):
new_list.append(rec_abs(el))
else:
new_list.append(abs(el))
return new_list
print(rec_abs([2,-6, [8,-12,-12, [4, [-6], -3]], 7, [3.55, -3.55]]))
[2, 6, [8, 12, 12, [4, [6], 3]], 7, [3.55, 3.55]]
For reference, modifying the list in-place is a simple, more efficient alternative, so you should consider this whenever possible. When iterating, iterate over the indices, and assign the return value to the ith index.
def rec_abs(a):
for i, el in enumerate(a):
a[i] = rec_abs(el) if isinstance(el, list) else abs(a[i])
return a
lst = [2,-6, [8,-12,-12, [4, [-6], -3]], 7, [3.55, -3.55]]
print(rec_abs(lst))
[2, 6, [8, 12, 12, [4, [6], 3]], 7, [3.55, 3.55]]

python get last 5 elements in list of lists

I have a list of lists like this: [[1, 2], [4, 5, 6], [], None, [7, 12, 14, 16]].
I want to write a function that will return: [16, 14, 12, 7, 6]: i.e. the last 5 elements in the list of lists.
This is the code I have, but it is not very pythonic at all (master_list contains the list above):
def find_last_five():
last_five = []
limit = 5
for sublist in reversed(master_list):
# have to check that list is not None.
if sublist:
for elem in sublist:
last_five.append(elem)
limit -= 1
if (limit == 0):
return last_five
return last_five
import itertools as it
a = [[1, 2], [4, 5, 6], [], [7, 12, 14, 16]]
reversed(it.islice(it.chain.from_iterable(reversed(a)), 5))
That actually assumes there are no None's in a. If there are just do a = filter(a, None).
Given your example; I will assume your items in your list are either an iterable or None;
>>> import itertools
>>> lst = [[1, 2], [4, 5, 6], [], None, [7, 12, 14, 16]]
>>> print list(itertools.chain(*[l for l in lst if l is not None]))[-5:]
[6, 7, 12, 14, 16]
You can use a list comprehension:
>>> tgt=[[1, 2], [4, 5, 6], [], None, [7, 12, 14, 16]]
>>> [e for sub in tgt if sub for e in sub][-5:]
[6, 7, 12, 14, 16]
That filters out the None. To filter out other non-list or tuples:
>>> [e for sub in tgt if isinstance(sub, (list, tuple)) for e in sub][-5:]
If you want something that does not have to flatten the entire list of lists first, you can just deal with the structure from the end and move up until you have what you want:
result=[]
current=[]
it=reversed(tgt)
while len(result)<5:
if current:
result.append(current.pop())
continue
else:
try:
current=next(it)
except StopIteration:
break
(Or use John 1024's solution)
Using no external modules:
master = [[1, 2], [4, 5, 6], [], None, [7, 12, 14, 16]]
new = []
total = 5
for x in reversed(master):
if x:
new += list(reversed(x))[:total-len(new)]
if total == len(new):
break
print(new)
This produces:
[16, 14, 12, 7, 6]
which is the desired list with the elements in the desired order.
Alternative approach using flatten recipe:
import collections
l = [[1, 2], [4, 5, 6], [], None, [7, 12, 14, 16]]
def flatten(l):
for el in l:
if isinstance(el, collections.Iterable) and not isinstance(el, str):
for sub in flatten(el):
yield sub
else:
yield el
print([v for v in flatten(l) if v][-5:])
# gives: [6, 7, 12, 14, 16]
How about a different approach?
a = [[1, 2], [4, 5, 6], [], None, [7, 12, 14, 16]]
sum(filter(None, a), [])[-1:-6:-1]
The filter function is necessary only because of the None type in the list. In case it is just a list of lists, this will be lot simpler to write like this:
sum(a, [])[-1:-6:-1]
The principle behind this? We actually use the '+' operator of list to just keep on adding the lists into a single list. Please note that this is not the way to choose(if you choose ;)) for longer lists. For smaller and medium lists, this is fine.
I'd use itertools to do this. Something like
list(itertools.chain.from_iterable(x for x in l if x is not None))[:-5]
where l is your input list.

Categories

Resources