Two or more positional arguments in lambda expression - python

Given following lists:
arr = [1, 5, 2, 6, 3, 7, 4]
cmds = [[1, 3, 2], [3, 5, 1], [2, 7, 4]]
For each command in cmds, I want to obtain
[arr[1:3][2], arr[3:5][1], arr[2:7][4]]
I tried:
list(map(lambda x: arr[x[0]:x[1]][x[2]], cmds))
But could I possibly write a cleaner code than the above? I want to use 3 positional arguments i, j, k instead of x alone:
list(map(lambda i, j, k: arr[i:j][k], cmds)) # This gives TypeError

Argument unpacking in function signatures has been removed in Python 3 (See this answer).
Here are some alternatives:
Solution 1:
If you want to get the same effect with clean code, #bereal's answer is a good choice:
[arr[i:j][k] for i, j, k in cmds]
This is a for loop / list comprehension, so unpacking is allowed.
Solution 2:
If you insist on using map, you can do so with nested lambdas:
list(map(lambda cmd: (lambda i,j,k: arr[i:j][k])(*cmd), cmds))
In the outer lambda, cmd is assigned an element in cmds, which corresponds to a list of length 3. The return value of this outer lambda is an IIFE that defines an inner lambda taking 3 arguments, which we pass in by unpacking the cmd argument from the outer lambda.
Solution 3:
This one's by #KellyBundy:
list(map(lambda i,j,k: arr[i:j][k], *zip(*cmds)))
In this case, this is equivalent to
arr = [1, 5, 2, 6, 3, 7, 4]
cmds = [[1, 3, 1], [3, 5, 1], [2, 7, 4]]
c1 = (1, 3, 2)
c2 = (3, 5, 7)
c3 = (1, 1, 4)
list(map(lambda i,j,k: arr[i:j][k], c1, c2, c3))

Related

Adding M0, M1, ... Mn between each two elements in a list

I would like to loop through a list, which is called output as shown below:
output = [0, 1, 2, 3, 4, 5, 6]
But I have to exclude the first and last elements [1, 2, 3, 4, 5]
Then I would like to add between each two elements M. For the first two elements M0, the second two elements M1 and so on.
x= [1, M0, 2, M1, 3, M2, 4, M3, 5]
The expected output =
[[1, M0, 2],[2, M1, 3],[3, M2, 4],[4, M3, 5]]
And could I solve it with (while, for, and if) instead of using multiple for loops? Could you please assist me?
Below is my try:
Python code:
output = [0, 1, 2, 3, 4, 5, 6]
Total = []
for i in output:
if i != output[0] and i != output[-1]:
Total.append(i)
print(Total)
for j in (Total):
print(j)
h = []
for x in Total:
for y in (x,'M'0):
h.append(y)
print(h)
Here is a solution you can try, using zip to create pairs & enumerate over that.
output = [0, 1, 2, 3, 4, 5, 6]
output = output[1:-1] # Remove first & last digit
print(
[[i, f'M{index}', j] for index, (i, j) in enumerate(zip(output, output[1:]))]
)
[[1, 'M0', 2], [2, 'M1', 3], [3, 'M2', 4], [4, 'M3', 5]]
output = [0, 1, 2, 3, 4, 5, 6]
l = []
#remove the first and last element, you also can use pop(output.pop(0), output.pop(-1))
for i in output[1:-1]:
l.append(i)
print(l)
i = 1
count=1
while i < len(l):
adding = f'M{count}'
l.insert(i, adding )
i += 2
print(l)
There is a roundrobin() function in either the more_itertools module, or as a recipe in the docs for the itertools module. Then you just need to construct the two lists — the original without the first and last elements, and the Mn sequence.
from more_itertools import roundrobin
output = [0, 1, 2, 3, 4, 5, 6]
h = roundrobin(
output[1:-1],
('M%d' % i for i in range(len(output) - 3)),
)
print(list(h))
Note that roundrobin() returns a generator; if you need to use it more than once, or if you want to print it out, convert the result to a list. If you're just going to iterate through it or otherwise use it just once, you can leave it as a generator.

Adjustable lambda to different lists

I am sorting different lists. For example:
One list has 3 integers: [[3, 4, 2], [3, 5, 3]]
The other has 4: [[3, 4, 2, 4], [3, 5,3 ,2]]
Like this there are more, ranging from two to five integers.
I could do this manually for every list, but since I'm still learning I could ask how this is done automatically!
So I have this code for rearranging the list in descending order
l.sort(key=lambda x: [x[0], x[1], x[2], x[3]], reverse = True)
This would work for the second example, but for the first example it would give:
IndexError: list index out of range
So my thought was something like this:
key = lambda x: [x[i] for i in range(len(l[0]))]
but it didn't work..
Does anybody knows if in this situation you can have an adjustable lambda?
I'm not quite sure if I get your question correctly, as you talk about one list in each case but then again show lists looking like lists of lists.
So for the simple case of a normal list, python provides sorting already, you don't need lambda:
>>> a = [3,2,7,1,11]
>>> b = sorted(a)
>>> b
[1, 2, 3, 7, 11]
>>> b = sorted(a, reverse=True)
>>> b
[11, 7, 3, 2, 1]
>>>
Check here for more options on sorting: https://docs.python.org/3/howto/sorting.html
Now if you have a list of lists, you could go for something like this:
>>> a = [[1,5,2],[3,4,1]]
>>> a
[[1, 5, 2], [3, 4, 1]]
>>> for idx,ls in enumerate(a):
a[idx] = sorted(ls, reverse=True)
>>> a
[[5, 2, 1], [4, 3, 1]]
which you could then use in a map function if you wanted to use a lambda:
>>> a = [[1,5,2],[3,4,1]]
>>> b = map(lambda x: sorted(x, reverse=True),a)
>>> b
<map object at 0x03C0F220>
>>> list(b)
[[5, 2, 1], [4, 3, 1]]
>>>
Note to put the list keyword infront of the map object to make it a list.

iteration in list with math operations

i have test list
a = [1,2,3,4,5,6,7,8,9,10]
for example, i want to squared some of them and get sum of them. then i want to divide this sum to 2. and then i want raise to the power 1/4
code is:
result = ((a[0]**2+a[1]**2)/2)**(1/4)
prolbem is that i define each value. in this example its a[0] and a[1]
i want just get some variable of number of iterable objects (in my case it's n = 2)
for n = 3 its should be equal to:
((a[0]**2+a[1]**2+a[2]**2)/2)**(1/4)
i can get this values with
for i in range(3):
print(a[i])
with output:
1
2
3
but idk how to add them to my math operation code
This should do it:
square=0
for i in range(3):
square=square+a[i]**2
if i==2:
square=(square/2)**(1/4)
print(square)
This squares each number and adds it to the square variable. If it is on the last iteration of i, it divides square by 2 and raises it to the power of 1/4
You can create a dictionary of operations to perform and apply them on part of your list.
this needs python 3.7+ to guarantee insert-order of rules in dict == order they get applied later:
a = [1,2,3,4,5,6,7,8,9,10]
# rules
ops = {"square_list" : lambda x: [a**2 for a in x], # creates list
"sum_list" : lambda x : sum(x), # skalar
"div_2" : lambda x : x/2, # skalar
"**1/4" : lambda x: x**(1/4)} # skalar
n_min = 0
for m in range(1,len(a)):
# get list part to operate on
parts = a [n_min:m]
print(parts)
# apply rules
for o in ops:
parts = ops[o](parts)
# print result
print(parts)
Output:
[1]
0.8408964152537145
[1, 2]
1.2574334296829355
[1, 2, 3]
1.6265765616977856
[1, 2, 3, 4]
1.9679896712654303
[1, 2, 3, 4, 5]
2.2899878254809036
[1, 2, 3, 4, 5, 6]
2.597184780029334
[1, 2, 3, 4, 5, 6, 7]
2.892507608519078
[1, 2, 3, 4, 5, 6, 7, 8]
3.1779718278112656
[1, 2, 3, 4, 5, 6, 7, 8, 9]
3.4550450628484315

Take elements from multiple lists

Given multiple lists like the ones shown:
a = [1, 2, 3]
b = [5, 6, 7, 8]
c = [9, 0, 1]
d = [2, 3, 4, 5, 6, 7]
...
I want to be able to combine them to take as many elements from the first list as I can before starting to take elements from the second list, so the result would be:
result = [1, 2, 3, 8, 6, 7]
Is there a particularly nice way to write this? I can't think of a really simple one without a for loop. Maybe a list comprehension with a clever zip.
Simple slicing and concatenation:
a + b[len(a):]
Or with more lists:
res = []
for lst in (a, b, c, d):
res += lst[len(res):]
# [1, 2, 3, 8, 6, 7]
With itertools.zip_longest() for Python 3, works on any number of input lists:
>>> from itertools import zip_longest
>>> [next(x for x in t if x is not None) for t in zip_longest(a,b,c,d)]
[1, 2, 3, 8, 6, 7]
The default fill value is None so take the first none None element in each tuple created with the zip_longest call (you can change the defaults and criteria if None is a valid data value)
With functools.reduce:
from functools import reduce
print(list(reduce(lambda a, b: a + b[len(a):], [a, b, c, d])))
This outputs:
[1, 2, 3, 8, 6, 7]

List of consecutive numbers repeated according to values in another list

My target is to get a list of consecutive numbers, repeated accordingly with the initial list values. Lets say I have:
initialList=[1,2,3,5]
And I want to get:
targetList=[0,1,1,2,2,2,3,3,3,3,3]
...I'm totally new with Python, sorry for this -probably- very first steps question. Tried many searchs but the results didn't match with my needs, unfortunately. Thank you very much in advance.
The newbie-friendly solution is to use two loops:
result = []
number = 0
for repeat in initialList:
for _ in range(repeat):
result.append(number)
number += 1
print(result) # [0, 1, 1, 2, 2, 2, 3, 3, 3, 3, 3]
If you prefer one-liners for whatever reason, you can combine enumerate and range to get
result = [num for num, repeat in enumerate(initialList) for _ in range(repeat)]
IMO, this is a more maintainable functional solution:
initialList = [1, 2, 3, 5]
def listify(x):
return [x]
# create sub-lists [[0], [1], [2], [3], ...]
sublists = map(listify, range(len(initialList)))
# attach to each sub-list the repititions required [([0], 1), ([2], 2), ...]
sublists_with_rep_spec = zip(sublists, initialList)
# create repetitions based on initialList (using list multiplication)
sublists_with_repetitions = starmap(operator.mul, sublists_with_rep_spec)
# flatten everything out
result = chain.from_iterable(sublists_with_repetitions)
print(list(result))
Note that this is all lazy (on python3) so everything will "happen" only when you actually call list.
Here is another way using repeat and chain.from_iterable
from itertools import repeat, chain
list(chain.from_iterable((repeat(idx, num)) for idx, num in enumerate(initialList)))
[0, 1, 1, 2, 2, 2, 3, 3, 3, 3, 3]
You can use enumerate:
initialList=[1,2,3,5]
final_result = [i for b in [[c]*d for c, d in enumerate(initialList)] for i in b]
Output:
[0, 1, 1, 2, 2, 2, 3, 3, 3, 3, 3]
This is possible via itertools, if you wish to remove the need for nested logic. itertools is part of the standard library.
For improving your understanding of Python, I recommend you see #Ajax1234's solution for some nested list comprehensions.
from itertools import chain
initialList = [1,2,3,5]
targetList = list(chain.from_iterable([i]*j for i, j in enumerate(initialList)))
# [0, 1, 1, 2, 2, 2, 3, 3, 3, 3, 3]
Note: you can replace [i]*j with itertools.repeat(i, j) or numpy.repeat(i, j) if you use numpy. The former may be preferable as it is lazy.
Very simple solution using sum and enumerate
initialList = [1, 2, 3, 5]
targetList = sum((times*[index] for index, times in enumerate(initialList)), [])
You can try this approach:
data=[[i]*initialList[i] for i,j in enumerate(initialList)]
print([k for i in data for k in i])
Just for fun I tried with lambda :
initialList=[1,2,3,5]
print(list(map(lambda x:[x]*initialList[x],range(0,len(initialList)))))
lambda result is in nested list.
My solution
>>> initialList=[1,2,3,5]
>>> sum(([num]*count for num, count in enumerate(initialList)), [])
[0, 1, 1, 2, 2, 2, 3, 3, 3, 3, 3]
Another easy way:
from functools import reduce
initialList = [1,2,3,5]
targetList = [[index]*item for index, item in enumerate(initialList)]
targetList = reduce(lambda x,y: x+y, targetList)
print(targetList)
# [0, 1, 1, 2, 2, 2, 3, 3, 3, 3, 3]
I find most of the current answers either poor performance-wise or hard to read. An alternative functional way of doing this would be by using such itertools functions as chain.from_iterable, repeat, and count:
from itertools import chain, count, repeat
initial_list = [1, 2, 3, 5]
result = list(chain.from_iterable(map(repeat, count(), initial_list)))
# [0, 1, 1, 2, 2, 2, 3, 3, 3, 3, 3]

Categories