1

I have a Python function which takes a parameter and returns 2 outputs. I want to call this for all the elements in a list and combine the return values for each invocation as below:

a1, b1 = func(p[0])
a2, b2 = func(p[1])
a3, b3 = func(p[3])

a = a1 & a2 & a3
b = b1 + b2 + b3

How can I refactor this to a one liner using the += , &= and the reduce operators in Python?

Thanks

locke14
  • 1,335
  • 3
  • 15
  • 36
  • Why do you specifically ask for the ``+=`` , ``&=`` and the ``reduce`` operators? By design, you cannot use ``+=`` and ``&=`` in the same statement (i.e. for practical purposes the same line). – MisterMiyagi Oct 28 '20 at 11:28

2 Answers2

2

Since you want a one-liner, something like this should help you:

a,b = int(all(ele == list(zip(*map(func,p)))[0][0] for ele in list(zip(*map(func,p)))[0])),sum(list(zip(*map(func,p)))[-1])

Breakdown:

The map function applies a function to an iterable.

list(map(func,p))

Prints:

[(1, 3), (2, 4), (3, 5)]

This:

list(zip([(1, 3), (2, 4), (3, 5)]))

Prints:

[((1, 3),), ((2, 4),), ((3, 5),)]

Adding an * before map would transpose the list:

list(zip(*map(func,p)))

Prints:

[(1, 2, 3), (3, 4, 5)]

The all function is combined with a simple list comprehension in order to find if all elements of sublist 1 are equal. Here is a simpler version of it:

all(ele == lst[0] for ele in lst) 

The same has been applied in this case like this:

all(ele == list(zip(*map(func,p)))[0][0] for ele in list(zip(*map(func,p)))[0])

Prints:

False

In order to convert True/False to 1/0, I have used the int() function:

int(all(ele == list(zip(*map(func,p)))[0][0] for ele in list(zip(*map(func,p)))[0]))

Prints:

0

The second part of it, which is this: sum(list(zip(*map(func,p)))[-1]) calculates the sum of the second sublist.

sum(list(zip(*map(func,p)))[-1])

Prints:

12

Here is the full code:

def func(a):
    return a,a+2

p = [1,2,3]

a,b = all(ele == list(zip(*map(func,p)))[0][0] for ele in list(zip(*map(func,p)))[0])),sum(list(zip(*map(func,p)))[-1])

print(a,b)

Output:

(0, 12)
Sushil
  • 5,440
  • 1
  • 8
  • 26
  • This does not appear to answer the question. – MisterMiyagi Oct 28 '20 at 11:31
  • Thanks for adding the missing code (I assume you will fix it to be completely runnable and correct). Though to be honest, I have no idea how it actually works – in specific, translating the ``&`` operation to ``all``, ``==`` and several ``list(zip(...)[0]...`` parts is not obvious to me. Perhaps you can edit the answer to explain how your approach works? – MisterMiyagi Oct 28 '20 at 11:55
  • 2
    Sure...I will do that. – Sushil Oct 28 '20 at 11:56
  • Check out my latest edit and let me know whether the explanation is sufficient. Will I get an upvote from u? – Sushil Oct 28 '20 at 12:12
1

We can decompose this problem into individual tasks:

  • Mapping a function func across the elements p[0], p[1], ... of an iterable/sequence p is map(func, p).
    • This already provides an iterable of (a1, b1), (a2, b2) so the assignment does not need special handling.
  • The chain of operations a1 & a2 & ... for a sequence/iterable a is reduce(lambda new, total: new & total) and likewise for +.
    • Since we have a stream of pairs of (a1, b1), ... instead of a pair of streams a1, ... and b1, ... we must operate on both at once.
  • The result of reducing a stream of pairs is a single pair, which we can assign to two variables as a, b = result.
from functools import reduce

def func(a): return a, a
p = range(1, 5, 2)


def reduction(new, total):
    return new[0] & total[0], new[1] + total[1]

mapped = map(func, p)
reduced = reduce(reduction, mapped)
a, b = reduced

If desired, this can be written in one statement/line.

>>> a, b = reduce(lambda new, total: (new[0] & total[0], new[1] + total[1]), map(func, p))
>>> a
1
>>> b
4
MisterMiyagi
  • 44,374
  • 10
  • 104
  • 119