map with a lambda function that accepts two parameters - python

I would like to fetch i'th element from all the sub lists in a list of lists.I tried using map and lambda function as below
extract = lambda x,i :x[i]
a = [[1,2,3],[4,5,6],[6,7,8]]
b = list(map(extract(i = 1),a))
I expect b to be [2,5,7]
but I know the last line doesn't work. How should I approach this with map and lambda

You can hard code in the 1:
extract = lambda x: x[1]
a = [[1,2,3],[4,5,6],[6,7,8]]
b = list(map(extract,a))
print(b)
# [2, 5, 7]
You normally don't want to store a lambda to a variable, this is better:
def extract(x):
return x[1]
b = list(map(extract, a))
Or simply this:
b = list(map(lambda x: x[1], a))
You can also use a list comprehension, which I personally think is the best option:
c = [x[1] for x in a]
print(b == c)
True

The underlying problem is your first function argument needs to be specified when you call extract. This is possible via functools.partial:
from functools import partial
b = list(map(partial(extract, i=1), a)) # [2, 5, 7]
But this is relatively inefficient, since a new function is created for each iteration of a. Instead, as others have advised, use operator.itemgetter:
from operator import itemgetter
b = list(map(itemgetter(1), a)) # [2, 5, 7]
As an aside, PEP 8 advises against naming lambda functions; define explicitly instead:
def extract(x, i):
return x[i]

You do not need to hard code the index.
IMHO, you should return a lambda function from extract method by doing something like this perhaps:
def extract(i):
return lambda x : x[i]
a = [[1,2,3],[4,5,6],[6,7,8]]
b = list(map(extract(1), a))
print(b)
Output:
[2, 5, 7]
Note: Better(read pythonic) approach will be to use list comprehension like this:
a = [[1,2,3],[4,5,6],[6,7,8]]
b = [li[1] for li in a]
print(b)

I would suggest using operator.itemgetter here to fetch the second item of each sublist:
from operator import itemgetter
a = [[1,2,3],[4,5,6],[6,7,8]]
print(list(map(itemgetter(1), a)))
# [2, 5, 7]
Or using lambda:
a = [[1,2,3],[4,5,6],[6,7,8]]
print(list(map(lambda x: x[1], a)))
# [2, 5, 7]
Your anonymous function:
extract = lambda x,i :x[i]
Needs to instead map specifically an index:
extract = lambda x: x[1]
Then you can simply map this function to your list with map(extract(1), a).

I also vote for the 'for' solution.
Functional programming syntax looks beautiful but its too much overhead sometimes.
a = [[1,2,3],[4,5,6],[6,7,8]]
b = list(map(lambda x: x[1], a)) # Brr, how many types conversions involved
c = [x[1] for x in a] # Looks more lightweight
Lets just check:
import timeit
timeit.timeit('a = [[1,2,3],[4,5,6],[6,7,8]]; b = [x[1] for x in a]', number=10000)
> 0.01244497299194336
timeit.timeit('a = [[1,2,3],[4,5,6],[6,7,8]]; b = list(map(lambda x: x[1], a))', number=10000)
> 0.021031856536865234
2 times slower.

Related

Binding of lambdas

def f(x):
return x <5
[x for x in filter(lambda x: f(x), ls)] #[0,1,2,3,4]
def g(x):
return lambda: x<5
[x for x in filter(lambda x: g(x), ls)] # [0,1,2,3,4,5,6,7,8,9]
def h(x):
return lambda x=x: x<5
[x for x in filter(lambda x: h(x), ls)] # [0,1,2,3,4,5,6,7,8,9]
Can anyone explain why g and h are not equivalent to f?
I think they should be equivalent because x in g and h should bind to the x in the environment that they are defined in (see this question on closures)
Here g and h return function object, but filter waits an function which returns a boolean or object wich will convert as boolean . With f it is correct for your expected output, but for g and h, the condition is always true because bool(function object) is always true see here, a python function is a callable object.
Take:
def f(x):
return x < 5
def g(x):
return lambda: x < 5
def h(x):
return lambda x=x: x < 5
I advise to do:
ls = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
print([x for x in ls if f(x)]) # list comprehension
# is equivalent to filter(lambda x: x < 5, ls) iterator
# To get list :
# [x for x in filter(lambda x: x < 5, ls)]
# is equivalent to list(filter(lambda x: x < 5, ls))
Output:
[0,1,2,3,4]
To call g to do:
g(1)() # True
Do:
print([x for x in ls if g(x)()])
Output:
[0,1,2,3,4]
To call h to do:
h(1)() # True
or
h(1)(5) # False the second argument overrides the first one
Do:
print([x for x in ls if h(x)(x)])
# or print([x for x in ls if h(x)()])
Output:
[0,1,2,3,4]
See documentation to use filter:
Construct a list from those elements of iterable for which function returns true. iterable may be either a sequence, a container which supports iteration, or an iterator. If iterable is a string or a tuple, the result also has that type; otherwise it is always a list. If function is None, the identity function is assumed, that is, all elements of iterable that are false are removed.
Note that filter(function, iterable) is equivalent to [item for item in iterable if function(item)] if function is not None and [item for item in iterable if item] if function is None.
See documentation to use lambda expression:
lambda arguments: expression with :
def <lambda>(arguments):
return expression
g(x) and h(x) are returning the lambda function itself (<function __main__.<lambda>>), but not executing it.
Try for example: g(3)() that will return the desired value.
Therefore running g(x)() or h(x)() will work:
[x for x in filter(lambda x: g(x)(), ls)] # [0,1,2,3,4]
[x for x in filter(lambda x: h(x)(), ls)] # [0,1,2,3,4]
While running only g(x) or h(x) will return the lambda function itself per each value, which is equivalent to True statement in filter function, and therefore no value is filtered.
and of course, in this case you can just run:
filter(lambda x: x<5, ls)
or even a better list comprehension approach:
[x for x in ls if x<5]

Python inplace update of function arguments?

I am trying to create a function that updates arguments inplace (mostly for curiosity's sake):
def inplaceUpdate(f, inputs):
for i in inputs:
i = f(i)
I have three inputs:
x = 1
y = 2
z = 3
And the function f:
f = lambda i: i**2
I would like to run the following code:
inplaceUpdate(f, [x, y, z])
Then I would like for the values of x, y, and z to change inplace. Is this possible?
x = 1
y = 4
z = 9
In Python, integers are immutables. There's an on-topic question here.
The idea is that you cannot change the value of the references x, y and z. That means that if you do:
x = 2
y = 3
z = 4
some_func([x, y, z])
There's no way that some_func changes the value of the variables x, y, and z.
However, lists are mutable, and you could do:
def some_func(l):
l[:] = [i*2 for i in l]
l = [2, 3, 4]
some_func(l)
print l # the array has changed
And this would indeed change the list. This is because the operation l[:]=... assigns to the containment of the list, instead of reassigning the reference --that would be l=....
Seems, what you whant is to map a list. There is beautiful builtin function map for that
# Declare lambda somewhere
f = lambda i: i**2
# Map your inputs
input = [1, 2, 3]
result = map(f, input)
You can also use list comprehensions to achieve this. Its more pythonic than map, but does essentially the same thing.
input = [1, 2, 3]
ls = [x**2 for x in input]
There is no way to modify the values of the variables of the calling function with out using ugly hackery. If you saved a reference to the mutable list you created in [x,y,z], the inplaceUpdate function could modify it.
To accomplish this task using ugly hacks:
def inPlaceUpdate(inputs):
frame = sys._getframe(1)
for input in inputs:
i = f(input)
for k,v in frame.f_locals.iteritems():
if v == input:
frame.f_locals[k] = i
break
else:
for k,v in frame.f_globals.iteritems():
if v == input:
frame.f_globals[k] = i
break

map,lambda and append.. why doesn't it work?

So I'm trying to do this.
a = []
map(lambda x: a.append(x),(i for i in range(1,5)))
I know map takes a function but so why doesn't it append to the list? Or is append not a function?
However printing a results to a still being empty
now an interesting thing is this works
a = []
[a.append(i) for i in range(5)]
print(a)
aren't they basically "saying" the same thing?
It's almost as if that list comprehension became some sort of hybrid list-comprehension function thing
So why doesn't the lambda and map approach work?
I am assuming you are using Python 3.x , the actual reason why your code with map() does not work is because in Python 3.x , map() returns a generator object , unless you iterate over the generator object returned by map() , the lambda function is not called . Try doing list(map(...)) , and you should see a getting filled.
That being said , what you are doing does not make much sense , you can just use -
a = list(range(5))
append() returns None so it doesn't make sense using that in conjunction with map function. A simple for loop would suffice:
a = []
for i in range(5):
a.append(i)
print a
Alternatively if you want to use list comprehensions / map function;
a = range(5) # Python 2.x
a = list(range(5)) # Python 3.x
a = [i for i in range(5)]
a = map(lambda i: i, range(5)) # Python 2.x
a = list(map(lambda i: i, range(5))) # Python 3.x
[a.append(i) for i in range(5)]
The above code does the appending too, however it also creates a list of None values as the size of range(5) which is totally a waste of memory.
>>> a = []
>>> b = [a.append(i) for i in range(5)]
>>> print a
[0, 1, 2, 3, 4]
>>> print b
[None, None, None, None, None]
The functions map and filter have as first argument a function reference that is called for each element in the sequence (list, tuple, etc.) provided as second argument AND the result of this call is used to create the resulting list
The function reduce has as first argument a function reference that is called for first 2 elems in the sequence provided as second argument AND the result is used together with the third elem in another call, then the result is used with the fourth elem, and so on. A single value results in the end.
>>> map(lambda e: e+10, [i for i in range(5)])
[10, 11, 12, 13, 14]
>>> filter(lambda e: e%2, [i for i in range(5)])
[1, 3]
>>> reduce(lambda e1, e2: e1+e2, [i for i in range(5)])
10
Explanations:
map example: adds 10 to each elem of list [0,1,2,3,4]
filter example: keeps only elems that are odd of list [0,1,2,3,4]
reduce example: add first 2 elems of list [0,1,2,3,4], then the result and the third elem of list, then the result and fourth elem, and so on.
This map doesn't work because the append() method returns None and not a list:
>>> a = []
>>> type(a.append(1))
<class 'NoneType'>
To keep it functional why not use reduce instead?
>>> from functools import reduce
>>> reduce(lambda p, x: p+[x], (i for i in range(5)), [])
[0, 1, 2, 3, 4]
Lambda function will not get triggered unless you wrap the call to map function in list() like below
list(map(lambda x: a.append(x),(i for i in range(1,5))))
map only returns a generator object which needs to be iterated in order to create a list. Above code will get the lambda called.
However this code does not make much sense considering what you are trying to achieve

How simple list functions could be implemented in Python

I know that Python has built-in list functions but I'm curious as to how one would write a function to sum a list and a function to reverse a list. I was able to figure out how to write some other list functions (sort, count, index, etc.) but not these, and I imagine some other languages don't have these built-in functions.
Could someone show me some Python code for those 2 functions, not using any other built-in functions?
For summing a list, you can do:
sum([1, 2, 3, 4])
And for reversing a list, this will return a new, reversed list by using Python's slices:
[1, 2, 3, 4][::-1]
Now if you don't want to use built-in functions:
def sum(lst):
s = 0
for e in lst:
s += e
return s
def reverse(lst):
l = []
for e in lst:
l = [e] + l
return l
Sum a list
Straight from the Python manual:
>>> def sum(seq):
... def add(x,y): return x+y
... return reduce(add, seq, 0)
>>> sum(range(1, 11))
55
>>> sum([])
0
This could be done as a one-liner (...ish) using lambda (Python's anonymous function syntax):
def sum(seq):
return reduce(lambda x, y: x + y, seq, 0)
Don't want to use reduce?
def sum(seq):
total = 0
for s in seq:
total += s
return total

Python: how to create a function pointer with a set argument?

My problem:
Given the following:
def foo(a,b)
I am trying to call the python 'map' function while passing in a list for 'a' but use a set value for 'b.'
Another relevant fact is that 'b' is user input and thus, I cannot use the syntax:
def foo(a,b='default value')
I want my 'map' call to look like this:
map(foo_wrapper,list_for_a)
where 'foo_wrapper' is some function that takes in 'a' but uses the user specified 'b.'
I don't know whether function pointers can be specified this way and suspect that they cannot.
My solution to this problem uses globals, so if there's a more elegant way and the above is impossible, I will mark that as the answer as well.
Here is my solution in a nutshell:
b = ''
def foo(a,b):
print b,a
def foo_wrapper(a):
foo(a,b)
def main():
if sys.argv[1]:
a = ['John', 'Jacob', 'Jingle all the way']
global b
b = sys.argv[1]
map(foo_wrapper,a)
There may be a typo or two in the above; I am simplifying the problem from what I actually need to do.
Thanks for the replies!
You can use functools.partial() for this purpose:
from functools import partial
def f(a, b):
return a + b
x = range(10)
print map(partial(f, b=3), x)
prints
[3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
You want something akin to currying. You can just use lambda here:
map(lambda x: f(x,3), a)
Use a list comprehension or a generator expression
[f(x, your_set) for x in your_list]
If you don't need a list as a result, but just a lazy evaluated iterator, you can use a generator expression (or if you meant Python 3's map).
(f(x, your_set) for x in your_list)
Edit:
For your functions that would be:
L = ['John', 'Jacob', 'Jingle all the way']
[foo(a, b=b) for a in L]
List comprehensions are a syntax sugar to replace uses of map with lambda. If you have one of the following:
L2 = map(lambda arg: f(arg) + arg, L1)
L2 = map(lambda (x,y): x + y, L1)
L2 = map(lambda <arg>: <expression>, L1)
They can be rewritten as list comprehensions:
L2 = [f(arg) + arg for arg in L1]
L2 = [x + y for x, y in L1]
L2 = [<expression> for <arg> in L1]
Generator expressions are similar, but instead of a list they return a lazy iterator, and are written with parens instead of square brackets. (And because map in Python 3 is changed to not return lists, there its equivalent is a generator expression.) Sometimes a list is not need, for example when you want to do:
','.join(map(lambda x: x.upper(), L))
The equivalent list comprehension is:
','.join([x.upper() for x in L])
But you actually don't need a list, so you can simply do:
','.join(x.upper() for x in L)

Categories