I am a python newbie trying to achieve the following:
I have a list of lists:
lst = [[567,345,234],[253,465,756, 2345],[333,777,111, 555]]
I want map lst into another list containing only the second smallest number from each sublist. So the result should be:
[345, 465, 333]
For example if I were just interested in the smallest number, I could do:
map(lambda x: min(x),lst)
I wish I could do this:
map(lambda x: sort(x)[1],lst)
but sort does not chain. (returns None)
neither is something like this allowed:
map(lambda x: sort(x); x[1],lst) #hence the multiple statement question
Is there a way to do this with map in python but without defining a named function? (it is easy with anonymous blocks in ruby, for example)
This question is related to
python
Yes it is possible. Try below code snippet.
x = [('human', 1), ('i', 2), ('am', 1), ('.', 1), ('love', 1), ('python', 3), ('', 1),
('run', 1), ('is', 2), ('robust', 1), ('hello', 1), ('spark', 2), ('to', 1), ('analysis', 2), ('on', 1), ('big', 1), ('data', 1), ('with', 1), ('analysis', 1), ('great', 1)
]
rdd_filter = rdd1_word_cnt_sum.filter(lambda x: 'python' in x or 'human' in x or 'big' in x)
rdd_filter.collect()
Time traveler here. If you generally want to have multiple statements within a lambda, you can pass other lambdas as arguments to that lambda.
(lambda x, f: list((y[1] for y in f(x))))(lst, lambda x: (sorted(y) for y in x))
You can't actually have multiple statements, but you can simulate that by passing lambdas to lambdas.
Edit: The time traveler returns! You can also abuse the behavior of boolean expressions (keeping in mind short-circuiting rules and truthiness) to chain operations. Using the ternary operator gives you even more power. Again, you can't have multiple statements, but you can of course have many function calls. This example does some arbitrary junk with a bunch of data, but, it shows that you can do some funny stuff. The print statements are examples of functions which return None
(as does the .sort()
method) but they also help show what the lambda
is doing.
>>> (lambda x: print(x) or x+1)(10)
10
11
>>> f = (lambda x: x[::2] if print(x) or x.sort() else print(enumerate(x[::-1]) if print(x) else filter(lambda (i, y): print((i, y)) or (i % 3 and y % 2), enumerate(x[::-1]))))
>>> from random import shuffle
>>> l = list(range(100))
>>> shuffle(l)
>>> f(l)
[84, 58, 7, 99, 17, 14, 60, 35, 12, 56, 26, 48, 55, 40, 28, 52, 31, 39, 43, 96, 64, 63, 54, 37, 79, 25, 46, 72, 10, 59, 24, 68, 23, 13, 34, 41, 94, 29, 62, 2, 50, 32, 11, 97, 98, 3, 70, 93, 1, 36, 87, 47, 20, 73, 45, 0, 65, 57, 6, 76, 16, 85, 95, 61, 4, 77, 21, 81, 82, 30, 53, 51, 42, 67, 74, 8, 15, 83, 5, 9, 78, 66, 44, 27, 19, 91, 90, 18, 49, 86, 22, 75, 71, 88, 92, 33, 89, 69, 80, 38]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99]
(0, 99)
(1, 98)
(2, 97)
(3, 96)
(4, 95)
(5, 94)
(6, 93)
(7, 92)
(8, 91)
(9, 90)
(10, 89)
(11, 88)
(12, 87)
(13, 86)
(14, 85)
(15, 84)
(16, 83)
(17, 82)
(18, 81)
(19, 80)
(20, 79)
(21, 78)
(22, 77)
(23, 76)
(24, 75)
(25, 74)
(26, 73)
(27, 72)
(28, 71)
(29, 70)
(30, 69)
(31, 68)
(32, 67)
(33, 66)
(34, 65)
(35, 64)
(36, 63)
(37, 62)
(38, 61)
(39, 60)
(40, 59)
(41, 58)
(42, 57)
(43, 56)
(44, 55)
(45, 54)
(46, 53)
(47, 52)
(48, 51)
(49, 50)
(50, 49)
(51, 48)
(52, 47)
(53, 46)
(54, 45)
(55, 44)
(56, 43)
(57, 42)
(58, 41)
(59, 40)
(60, 39)
(61, 38)
(62, 37)
(63, 36)
(64, 35)
(65, 34)
(66, 33)
(67, 32)
(68, 31)
(69, 30)
(70, 29)
(71, 28)
(72, 27)
(73, 26)
(74, 25)
(75, 24)
(76, 23)
(77, 22)
(78, 21)
(79, 20)
(80, 19)
(81, 18)
(82, 17)
(83, 16)
(84, 15)
(85, 14)
(86, 13)
(87, 12)
(88, 11)
(89, 10)
(90, 9)
(91, 8)
(92, 7)
(93, 6)
(94, 5)
(95, 4)
(96, 3)
(97, 2)
(98, 1)
(99, 0)
[(2, 97), (4, 95), (8, 91), (10, 89), (14, 85), (16, 83), (20, 79), (22, 77), (26, 73), (28, 71), (32, 67), (34, 65), (38, 61), (40, 59), (44, 55), (46, 53), (50, 49), (52, 47), (56, 43), (58, 41), (62, 37), (64, 35), (68, 31), (70, 29), (74, 25), (76, 23), (80, 19), (82, 17), (86, 13), (88, 11), (92, 7), (94, 5), (98, 1)]
Use sorted function, like this:
map(lambda x: sorted(x)[1],lst)
Putting the expressions in a list may simulate multiple expressions:
E.g.:
lambda x: [f1(x), f2(x), f3(x), x+1]
This will not work with statements.
After analyzing all solutions offered above I came up with this combination, which seem most clear ad useful for me:
func = lambda *args, **kwargs: "return value" if [
print("function 1..."),
print("function n"),
["for loop" for x in range(10)]
] else None
Isn't it beautiful? Remember that there have to be something in list, so it has True value. And another thing is that list can be replaced with set, to look more like C style code, but in this case you cannot place lists inside as they are not hashabe
There are better solutions without using lambda function. But if we really want to use lambda function, here is a generic solution to deal with multiple statements: map(lambda x: x[1] if (x.sort()) else x[1],lst)
You don't really care what the statement returns.
to demonstrate the lambda x:[f1(),f2()]
effect which enables us to execute multiple functions in lambda
. it also demonstrates the single line if else conditions if you really want to shrink the code.
lambda x:[exec('a=[1]'),exec('b=2')]
a python implementation of touch(linux) command which creates empty files if they are not already existing.
def touch(fpath):
check= os.path.exists(fpath)
(lambda fname1:[open(fname1,"w+",errors="ignore").write(""),print('Touched',fname1)]
if not check else None) (fpath)
will print [ Touched fpath
] where fpath
is file path given as input. will do nothing if file already exist.
the (lambda x: [ f(x), f2(x) ] ) (inp)
<- we pass the 'inp
' as input to lambda
which in this case is the fpath
.
Or if you want to avoid lambda and have a generator instead of a list:
(sorted(col)[1] for col in lst)
I'll give you another solution, Make your lambda invoke a function.
def multiple_statements(x, y):
print('hi')
print('there')
print(x)
print(y)
return 1
junky = lambda x, y: multiple_statements(x, y)
junky('a', 'b');
You can do it in O(n) time using min and index instead of using sort or heapq.
First create new list of everything except the min value of the original list:
new_list = lst[:lst.index(min(lst))] + lst[lst.index(min(lst))+1:]
Then take the min value of the new list:
second_smallest = min(new_list)
Now all together in a single lambda:
map(lambda x: min(x[:x.index(min(x))] + x[x.index(min(x))+1:]), lst)
Yes it is really ugly, but it should be algorithmically cheap. Also since some folks in this thread want to see list comprehensions:
[min(x[:x.index(min(x))] + x[x.index(min(x))+1:]) for x in lst]
A Hacky way to combine multiple statements into a single statement in python is to use the "and" keyword as a short-circuit operator. Then you can use this single statement directly as part of the lambda expression.
This is similar to using "&&" as the short-circuit operator in shell languages such as bash.
Also note: You can always fix a function statement to return a true value by wrapping the function.
Example:
def p2(*args):
print(*args)
return 1 # a true value
junky = lambda x, y: p2('hi') and p2('there') and p2(x) and p2(y)
junky("a", "b")
On second thought, its probably better to use 'or' instead of 'and' since many functions return '0' or None on success. Then you can get rid of the wrapper function in the above example:
junky = lambda x, y: print('hi') or print('there') or print(x) or print(y)
junky("a", "b")
'and' operate will evaluate the expressions until it gets to the first zero return value. after which it short-circuits. 1 and 1 and 0 and 1 evaluates: 1 and 1 and 0, and drops 1
'or' operate will evaluate the expressions until it gets to the first non-zero return value. after which it short-circuits.
0 or 0 or 1 or 0 evaluates 0 or 0 or 1, and drops 0
This is exactly what the bind
function in a Monad is used for.
With the bind
function you can combine multiple lambda's into one lambda, each lambda representing a statement.
Let me present to you a glorious but terrifying hack:
import types
def _obj():
return lambda: None
def LET(bindings, body, env=None):
'''Introduce local bindings.
ex: LET(('a', 1,
'b', 2),
lambda o: [o.a, o.b])
gives: [1, 2]
Bindings down the chain can depend on
the ones above them through a lambda.
ex: LET(('a', 1,
'b', lambda o: o.a + 1),
lambda o: o.b)
gives: 2
'''
if len(bindings) == 0:
return body(env)
env = env or _obj()
k, v = bindings[:2]
if isinstance(v, types.FunctionType):
v = v(env)
setattr(env, k, v)
return LET(bindings[2:], body, env)
You can now use this LET
form as such:
map(lambda x: LET(('_', x.sort()),
lambda _: x[1]),
lst)
which gives: [345, 465, 333]
Using begin() from here: http://www.reddit.com/r/Python/comments/hms4z/ask_pyreddit_if_you_were_making_your_own/c1wycci
Python 3.2 (r32:88445, Mar 25 2011, 19:28:28)
[GCC 4.5.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> lst = [[567,345,234],[253,465,756, 2345],[333,777,111, 555]]
>>> begin = lambda *args: args[-1]
>>> list(map(lambda x: begin(x.sort(), x[1]), lst))
[345, 465, 333]
Yes. You can define it this way and then wrap your multiple expressions with the following:
Scheme begin:
begin = lambda *x: x[-1]
Common Lisp progn:
progn = lambda *x: x[-1]
There actually is a way you can use multiple statements in lambda. Here's my solution:
lst = [[567,345,234],[253,465,756, 2345],[333,777,111, 555]]
x = lambda l: exec("l.sort(); return l[1]")
map(x, lst)
You can in fact have multiple statements in a lambda expression in python. It is not entirely trivial but in your example, the following works:
map(lambda x: x.sort() or x[1],lst)
You have to make sure that each statement does not return anything or if it does wrap it in (.. and False). The result is what is returned by the last evaluation.
Example:
>>> f = (lambda : (print(1) and False) or (print(2) and False) or (print(3) and False))
>>> f()
1
2
3
Source: Stackoverflow.com