Given an array a = [1,2,3,[4,5]] using Python 3, how can I add all the elements in the array?
sum(a[0])
sun(a[0][1])
The above code did not work. Also in the case, if you're given an array with an unknown amount of arrays inside arrays, How can those numbers be calculated?
def xsum(x):
if not x:
return 0
head, *tail = x
if isinstance(head, list):
return xsum(head) + xsum(tail)
elif tail:
return head + xsum(tail)
else:
return head
You need a flatten function. Here is one:
def flatten(a):
"""generator of flattened n-deep iterable (excluding str) a."""
for elem in a:
if not isinstance(elem, str):
try:
yield from flatten(elem)
except TypeError:
yield elem
else:
yield elem
which you can then use in sum, for example:
a = [1, 2, 3, [4, [5, 6]]
print(list(flatten(a))) # --> [1, 2, 3, 4, 5, 6]
print(sum(flatten(a))) # --> 21
You can use functools.reduce to sum this nested list
>>> from functools import reduce
>>> a = [1,2,3,[4,5]]
>>> reduce(lambda x,y: x + (sum(y) if type(y)==list else y), [0]+a)
15
If the list can be more than one level nested, you have to use a recursive approach
>>> f = lambda x,y: x + (reduce(f, y) if type(y)==list else y)
>>> reduce(f, [0]+a)
15
You can use the closure property for finding sum of infinite nested list.
def nested_list_sum(x):
c = []
def l_sum(x):
for i in x:
if isinstance(i, list):
l_sum(i)
else:
c.append(i)
l_sum(x)
return sum(c)
like
a = [1,2,3,[4,5]] ----> 15
a = [1,2,3,[4,5, [6,7, [8, 9]]], [10, 11, 12, [13, 14, 5]]] -- > 110
Related
I want to add two numbers to the list next to each other and return the final answer in a list using python.
for example
my_list = [1,2,3,5,6]
And the final list will be [1,3,5,8,11] for the first digit I will consider the initial value as 0.
I tried this way to get the answer
list1 = [1,2,3,0,5,6]
first_value = 0
new_list = []
for i,each_value in enumerate(list1):
if i == 0:
sum = first_value + each_value
else:
sum = each_value + prev_val
new_list.append(sum)
#i = i+1
prev_val = each_value
print(new_list)
I have got the output that I want but I wanted to know can we write the same code with less number of lines either by using lambda or some python collection.
Try this.
l = [1,2,3,5,6]
def sum_(l):
return list(map(lambda e:l[e]+l[e-1] if e!=0 else l[e], range(len(l))))
print(sum_(l))
OUTPUT [1, 3, 5, 8, 11]
Explanation
I suggest After reading this you need to take a look at the map function.
I give the map function two parameters one of them is range(len(l)) If you try to print In the above case this will output to range(0,5) Try to transform this into the list list(range(len(l))) this will output [0,1,2,3,4].
Map function is iterated through this list and set the value of e(provide this is a parameter in lambda function. You can change it to any other name.)
In line map(lambda e:l[e]+l[e-1] if e!=0 else l[e]. Here I am matching if e is not equal to 0, Because if e is 0 then e-1 is -1 which will point to the last element of the list Which will cause the code to output the wrong output.
In my code if e equals 0 then this will return lst[e] else this will return the lst[e]+lst[e-1].
I hope this will explain this 😉.
You can use a simple zip + map/sum:
my_list = [1,2,3,5,6]
out = list(map(sum, zip([0]+my_list, my_list)))
output: [1, 3, 5, 8, 11]
Or with itertools.pairwise (python ≥ 3.10):
from itertools import pairwise
out = my_list[:1]+list(map(sum, pairwise(my_list)))
In python3.10+, I like to do this:
>>> from itertools import pairwise, starmap
>>> from operator import add
>>> lst = [1, 2, 3, 5, 6]
>>> out = lst[:1]
>>> out.extend(starmap(add, pairwise(lst)))
>>> out
[1, 3, 5, 8, 11]
Some test:
from itertools import pairwise, starmap
from operator import add
from timeit import timeit
def starmap_add_extend(lst: list):
out = lst[:1]
out.extend(starmap(add, pairwise(lst)))
return out
def map_sum_extend(lst: list):
out = lst[:1]
out.extend(map(sum, pairwise(lst)))
return out
def starmap_add_concat(lst: list):
return lst[:1] + list(starmap(add, pairwise(lst)))
def map_sum_concat(lst: list):
return lst[:1] + list(map(sum, pairwise(lst)))
def comperhension_concat(lst: list):
return lst[:1] + [i + j for i, j in pairwise(lst)]
def generator_extend(lst: list):
out = lst[:1]
out.extend(i + j for i, j in pairwise(lst))
return out
def comperhension(lst: list):
return [val + lst[i - 1] if i else val for i, val in enumerate(lst)]
if __name__ == '__main__':
my_lst = [1, 2, 3, 5, 6]
for func in (
starmap_add_extend,
map_sum_extend,
starmap_add_concat,
map_sum_concat,
comperhension_concat,
generator_extend,
comperhension
):
print(func.__name__.ljust(20), timeit(lambda: func(my_lst)))
Output:
starmap_add_extend 0.5148536000051536
map_sum_extend 0.6762464999919757
starmap_add_concat 0.530809899995802
map_sum_concat 0.7072772000101395
comperhension_concat 0.545955400011735
generator_extend 0.7146139999968
comperhension 0.6786505000200123
I'm trying to find the next maximum value of nested lists, I already have a nested list sorted by bubblesort, I need to take the largest element of each nested list and insert it into the solution vector, until the solution vector is sorted.
P.S: I can't delete the element from the initial nested list, only find the next maximum value.
See the image at the bottom as an example:
Nested_list = [[1, 7, 9], [4, 5, 6], [2, 3, 8], [0]]
The way I devised deleted the largest vector from the original list, which was quite time consuming, I believe that just moving the index to the next largest value will consume less time:
def bubbleSort(array):
n = len(array)-1
for i in range(n):
for j in range(0, n-i):
if array[j] > array[j+1]:
array[j], array[j+1] = array[j+1], array[j]
else:
continue
return array
def ordena_lista(output):
for sublista in output:
bubbleSort(sublista)
def maior_valor_lista(output):
return list(el[-1] for el in output)
def nested_remove(L, x):
if x in L:
L.remove(x)
else:
for element in L:
if type(element) is list:
nested_remove(element, x)
b = list(random.sample(range(10), 10))
n= m.floor(m.sqrt(len(b)))
output=list([b[i:i + n] for i in range(0, len(b), n)])
ordena_lista(b)
while output:
valores_maximo = maior_valor_lista(output)
var = max(valores_maximo, key=int)
final = [var] + final
nested_remove(output, var)
output = list(filter(None, output))
The simplest solution would be the following,
from functools import reduce
from operator import add
def sort_nested_list(nested_list):
return sorted(reduce(add, nested_list))
but, without knowing the exact implementation details of python's sorted, I can't tell you if it takes advantage of your pre-sorting.
If we know the sublists are sorted, and we are allowed to copy the list, and we know how many elements there are in total, we can write the following,
import math
from copy import deepcopy
def get_max_and_pop(nested_list):
""" find the maximum element of a sublist of nested_list, remove it from the sublist, and return it """
print(f"get_max_and_pop says: {nested_list}")
return max(nested_list, key=lambda x: x[-1:]).pop()
def sort_nested_list_whose_sublists_are_sorted(nested_list, n_elements):
nested_list_copy = deepcopy(nested_list)
return [get_max_and_pop(nested_list=nested_list_copy) for _ in range(n_elements)][::-1]
edit: without knowledge of the number of elements, we can write,
from copy import deepcopy
def sort_nested_list_whose_sublists_are_sorted_iter(nested_list):
nested_list_copy = deepcopy(nested_list)
while any(nested_list_copy):
yield max(nested_list_copy, key=lambda x: x[-1:]).pop()
This amounts to a bizarre, woefully inefficient and completely unnecessary sorting algorithm but here goes anyway:
Nested_list = [[9, 7, 1], [4, 5, 6], [2, 3, 8], [0]]
for e in Nested_list:
e.sort()
Output_list = []
Nested_list_copy = [[e_ for e_ in e] for e in Nested_list]
element_count = sum(len(e) for e in Nested_list)
for _ in range(element_count):
m = None
for i, e in enumerate(Nested_list_copy):
if e:
tm = e[-1]
if m is None or tm > m:
m = tm
k = i
Output_list.insert(0, Nested_list_copy[k].pop())
print(Nested_list)
print(Output_list)
Output:
[[1, 7, 9], [4, 5, 6], [2, 3, 8], [0]]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
I have the following variables:
a = [1, 2, 3]
b = "de" # <-- not a (usual) list !
c = 5 # <-- not a list !
d = [4, 5, 23, 11, 5]
e = ["dg", "kuku"]
Now I want to concat all a, b, c, d, e to one list:
[1, 2, 3, "de", 5, 4, 5, 23, 11, 5, "dg", "kuku"]
I have tried itertools.chain but it didn't work. Please advise how can I make the concatenation?
chain works with iterables. What you mean is: concatenate these lists and raw values.
I see two steps:
def ensure_list(x):
if isinstance(x, list):
return x
return [x]
lists = map(ensure_list, (a, b, c, d, e))
concatenated = list(itertools.chain.from_iterable(lists))
You could define a function that takes an arbitrary number of arguments and iteratively constructs a list out of them depending on their type like this:
a = [1, 2, 3]
b = "de" # <-- not a (usual) list !
c = 5 # <-- not a list !
d = [4, 5, 23, 11, 5]
e = ["dg", "kuku"]
def concat(*args):
out = []
for arg in args:
if isinstance(arg, list):
out.extend(arg)
else:
out.append(arg)
return out
print(concat(a,b,c,d,e))
Outputs:
[1, 2, 3, 'de', 5, 4, 5, 23, 11, 5, 'dg', 'kuku']
Alternatively you could map over the list of args, ensure they're all a list, then use itertools.chain to combine the map object like this:
def concat(*args):
return list(itertools.chain(*map(lambda x : x if isinstance(x, list) else [x], args)))
print(concat(a,b,c,d,e))
Outputs:
[1, 2, 3, 'de', 5, 4, 5, 23, 11, 5, 'dg', 'kuku']
And here's a much more opaque way of accomplishing the same thing just for fun in a list comprehension:
def concat(*args):
return [x
for arg in args
for x in (arg if isinstance(arg, list) else [arg])]
print(concat(a,b,c,d,e))
Outputs:
[1, 2, 3, 'de', 5, 4, 5, 23, 11, 5, 'dg', 'kuku']
You could also create a generator with map that yields either the argument or the argument in a list and then sum it all together with a list (you probably shouldn't actually do this, but it's neat).
def concat(*args):
return sum(map(lambda arg : arg if isinstance(arg,list) else [arg], args), [])
print(concat(a,b,c,d,e))
Outputs:
[1, 2, 3, 'de', 5, 4, 5, 23, 11, 5, 'dg', 'kuku']
You have to combine append() and extend() because one of your examples is not a list (b and c) but a single integer.
#!/usr/bin/env python3
a = [1, 2, 3]
b = "de"
c = 5
d = [4, 5, 23, 11, 5]
e = ["dg", "kuku"]
the_input = [a, b, c, d, e]
result = []
for element in the_input:
if isinstance(element, list):
result.extend(element)
else:
result.append(element)
print(result)
I am not aware of any chain like method to improve that example.
I wouldn't recommend this, but here's another way of doing it if you like list comprehensions or one-liners:
to_concat = [a, b, c]
concatenated_list = []
concatenated_list += [item for sublist in [[list_or_val] if not isinstance(list_or_val, list) else list_or_val for list_or_val in to_concat] for item in sublist]
Output with a, b, c = "de", [1, 2], ["dg", "kuku"] :
In [6]: concatenated_list
Out[6]: ['de', 1, 2, 'dg', 'kuku']
How does it work?
This part:
[[list_or_val] if not isinstance(list_or_val, list) else list_or_val for list_or_val in to_concat]
of the list comprehension creates a new list by transforming non-list values (like a in our case) into lists (so "de" becomes ["de"]). Let's call it list_of_lists. We now want to flatten list_of_lists to get our end result, and we can do so by the other part of the list comprehension:
[item for sublist in list_of_lists for item in sublist]
(More info here on the flattening if you're interested)
As I've said, I wouldn't recommend this solution. It's quite a mess to understand, and it probably has terrible performance, so it's not suited to larger workloads.
Great question - it lead to making something I will be putting in my utilities toolbox:
Custom generator - chainanything()
I would create the helper function as a generator rather than returning a list. This keeps it more flexible for usage and is usually a tiny bit faster. I usually do this if I have a function that returns a list.
def chainanything(*args, preservestrings=True, recursive=False):
"""
Generator: yields the contents of a Sequence, or the given object if not a Sequence, one at a time
preservestrings = False will lead to strings being yielded as individual characters. Default = True
recursive = True will recursively flatten sequences. Default = False
Note: preservestrings = False, recursive = False will only flatten strings which are not part of another Sequence.
e.g.: 'abc' -> 'a','b','c' but ['ab','cd'] -> 'ab','cd'
"""
args = [*args]
for arg in args:
if not isinstance(arg, Sequence):
yield arg
else:
if preservestrings and isinstance(arg, str):
yield arg
elif recursive:
yield from flatten(arg)
else:
yield from arg
this can then be used in it's standard form to provide your expected result:
def test_preservestring():
# https://stackoverflow.com/questions/72288401/how-to-concat-lists-integers-and-strings-into-one-string/72288721#72288721
a = [1, 2, 3]
b = "de" # <-- not a (usual) list !
c = 5 # <-- not a list !
d = [4, 5, 23, 11, 5]
e = ["dg", "kuku"]
assert [x for x in chainanything(a,b,c,d,e)] == [1, 2, 3, "de", 5, 4, 5, 23, 11, 5, "dg", "kuku"]
Or with join and map to answer the question in the title and concatenate them into a string:
def test_join():
a = [1, 2, 3]
b = "de" # <-- not a (usual) list !
c = 5 # <-- not a list !
d = [4, 5, 23, 11, 5]
e = ["dg", "kuku"]
assert ''.join(map(str,chainanything(a,b,c,d,e))) == "123de54523115dgkuku"
The overall function came out a little longer than one line in order to handle strings in a logical way.
The flatten function recursively flattens sequences - it's another little helper generator I created for my toolbox:
def flatten(seq):
"""
Recursively flattens a sequence (including strings!) and returns all elements in order left to right.
E.g.: [1,2,[3,4,[5],6],7,[8,9]] -> [1,2,3,4,5,6,7,8,9]
"""
for item in seq:
if not isinstance(item, Sequence):
yield item
elif len(item) == 1 and item[0] == item: #eg item = 'a'
yield item[0]
else:
yield from flatten(item)
You can grab the latest version of the helpers here: https://dev.azure.com/MusicalNinjas/_git/MikesMath
So for example, I have the list = [1, [2, [3, [5, [5]]]]] the resulting list would be [1,3].
I currently have this,
def avg(mylist):
if mylist == []:
return mylist
elif type(mylist[0]) == list:
mylist[0] = 0 # average of mylist[0]
return mylist[:1]+avg(mylist[1:])
elif type(mylist[0]) == float:
return mylist[:1]+avg(mylist[1:])
Which works the way I want it too but I cannot find a way to set mylist[0] = the average of mylist[0]. I have also tried it a lot of different ways but I cannot find one that works.
EDIT: Another example of something I tried.
total = 0
nosublist=True
if mylist == []:
return mylist
for x in mylist:
if type(x) == list:
nosublist=False
if nosublist:
return mylist[:1]+average(mylist[1:])
elif not nosublist:
for x in mylist:
if type(x) == list:
total += average(x)
else:
total += x
mylist[0] = total/len(mylist)
return average(mylist[:1])+average(mylist[1:])
def isiter(x):
try:
iter(x)
return True
except TypeError:
return False
def _flatten(x, reduce=iter):
for i in x:
if isiter(i):
r = reduce((j for j in _flatten(i, reduce=reduce)))
if isiter(r):
yield from r
else:
yield r
else:
yield i
Now you can plugin mean
def mean(x):
l = list(x)
return sum(l)/len(l)
l = [1, [2, [3, [5, [5]]]]]
list(_flatten(l, reduce=mean))
>>> [1, 3.0]
or
mean(flatten(l, reduce=mean))
>>> 2.0
EDIT:
If you really need only a single function:
def flatten(x, reduce=iter):
return reduce(_flatten(x, reduce=reduce))
This isn't probably the best solution but you could use it help make yours even better! I created two lists on each recursive call one with only elements that aren't lists and one with elements that were all lists (just in case you had a format like [1, 3, [2], [2], [3, [5, [5, 5]]]]) and created a call stack to take the sum of the elements in each inner array and one to take the length of each inner array and then took the average. Of course since there might be multiple arrays in the list of arrays you can map this same functionality for each array and accumulate their avg's.
code
list1 = [1, [2, [3, [5, [5]]]]]
list2 = [1, [2, 3], [4, 5], [5, [3, 4]]]
def avg(mylist):
"""flattens an array where the sublists to flatten are the average of that sublist"""
subarrays = filter(lambda x: type(x) == type([]), mylist)
rootelems = filter(lambda x: type(x) != type([]), mylist)
avg_all = lambda elem: sum((avg(elem))) / len(avg(elem))
if subarrays == []:
return mylist
return rootelems + map(avg_all, subarrays)
print avg(list1)
print avg(list2)
result
[1, 3]
[1, 2, 4, 4]
For example, I have a list:
[[1,3],[23,4],[13,45,6],[8,3],[44,33,12]]
Is there any efficient way that I can finally get the list below?
[[1,3],[13,45,6]]
For every length of the list, keep only one element.
Just make a dictionary keyed off the length and get its values:
>>> l = [[1,3],[23,4],[13,45,6],[8,3],[44,33,12]]
>>> dict((len(i), i) for i in l).values()
[[8, 3], [44, 33, 12]]
s = set()
[e for e in l if len(e) not in s and s.add(len(e)) is None]
And then there is always explicit recursion ...
>>> l = [[1,3],[23,4],[13,45,6],[8,3],[44,33,12]]
>>> def f(x):
... if x == []: return []
... else : return [x[0]] + f( filter(lambda m:len(m)!=len(x[0]),x) )
...
>>> f(l)
[[1, 3], [13, 45, 6]]
map the list to set of element lengths and pull out elements with coresponding length, something like this:
#!/usr/bin/py3
#coding: utf-8
def uniqlens (ls):
for length in set(map(lambda x: len(x), ls)):
for elem in ls:
if len(elem) == length:
yield elem
break
coll = [[1,3],[23,4],[13,45,6],[8,3],[44,33,12]]
print(list(uniqlens(coll)))
# [[1, 3], [13, 45, 6]]