Python difference between filter() and map() - python

Being new to python I am just trying to figure out the difference between filter() and map().
I wrote a sample script as follows:
def f(x): return x % 2 == 0
def m(y): return y * 2
list = [1,2,3,4]
flist = filter(f, list)
print(list)
print(flist)
mlist = map(m, list)
print(list)
print(mlist)
We see that to both the filter and map we pass a list and assign their output to a new list.
Output of this script is
[1, 2, 3, 4]
[2, 4]
[1, 2, 3, 4]
[2, 4, 6, 8]
Question arises is that function call of both filter and map looks same so how will they behave if we interchange the contents of functions passed to them.
def f(x): return x * 2
def m(y): return y % 2 == 0
list = [1,2,3,4]
flist = filter(f, list)
print(list)
print(flist)
mlist = map(m, list)
print(list)
print(mlist)
This results in
[1, 2, 3, 4]
[1, 2, 3, 4]
[1, 2, 3, 4]
[False, True, False, True]
This shows filter evaluates the function and if true it returns back the passed element.
Here the function
def f(x): return x * 2
evaluates to
def f(x): return x * 2 != 0
In contrast map evaluates the function expression and returns back the result as items.
So filter always expects its function to do comparison type of task to filter out the elements while map expects its functions to evaluate a statement to get some result.
Is this understanding correct?

They both work a little bit differently but you've got the right idea.
Map takes all objects in a list and allows you to apply a function to it
Filter takes all objects in a list and runs that through a function to create a new list with all objects that return True in that function.
Here's an example
def square(num):
return num * num
nums = [1, 2, 3, 4, 5]
mapped = map(square, nums)
print(*nums)
print(*mapped)
The output of this is
1 2 3 4 5
1 4 9 16 25
Here's an example of filter
def is_even(num):
return num % 2 == 0
nums = [2, 4, 6, 7, 8]
filtered = filter(is_even, nums)
print(*nums)
print(*filtered)
The output of this would be
2 4 6 7 8
2 4 6 8

In map: Function will be applied to all objects of iterable.
In filter: Function will be applied to only those objects of iterable who goes True on the condition specified in expression.

As per my understanding below are the difference between map and filter:
def even(num):
if(num % 2 == 0):
return 'Even'
num_list = [1,2,3,4,5]
print(list(filter(even,num_list))) ->>>>>>>output: [2, 4]
print(list(map(even,num_list))) ->>>>>>> output: [None, 'Even', None, 'Even', None]
So, we can say that:
filter(): formats new list that contains elements which satisfy specific condition.
map(): function iterates through a all items in the given iterable and executes a function which we passed as an argument.

I think yes you got the picture pretty much.
both Map and filter are ways of applying function to iterables.
in Map you can use multiple iterables
definition : map(function_object, iterable1, iterable2,...)
whereas
in filter only one iterable can be used
definition : filter(function_object, iterable)
further in filter the function_object has to return boolean only.
for sake of example following is the Map with multiple iterables as input
list_a = [1, 2, 3]
list_b = [10, 20, 30]
map(lambda x, y: x + y, list_a, list_b) # Output: [11, 22, 33]

The filter() and map() functions are a little bit different.
While Maps takes a normal function, Filter takes Boolean functions. As a matter of fact, filter are maps with conditional logic, a Boolean logic.

Your example is too accurate.
In filter function your supposed to pass a function and a list(the function must evaluate to true or false). If the element passed in the function returns true the filter function will put the element passed into a new list. Where as map function will take an element pass it through a function and return the output of the function and store that to the new list.

map(): Function will be applied to all objects of iterable, we can use as many literables as wee needed
filter(): Function will be applied to only those objects of iterable and added to result which item is True, we can use only one literable
In the below, code 0 is not add in the filter function because 0 is a representation for False in some cases so it is not added to the filter and added in the map function result
def check(num):
return num*1
nums = [0,2, 4, 6, 7, 8]
result = filter(check, nums)
print(list(result))
def check(num):
return num*1
nums = [0,2, 4, 6, 7, 8]
result = map(check, nums)
print(list(result))

map() applies any applicable logic presented to any number of arguments of type list and returns an iterable containing values mapped to each respective members of the argument list(s).
example:
m = map(lambda x,y: 10+x+y, [1,2,3,4],[10,20,30,40])
print(list(m))
output:
[21, 32, 43, 54]
filter() applies the condition specified to one argument of type list and returns an iterable containing values that satisfy the specified condition and thus selected from the argument.
example:
f = filter(lambda x: x<3, [1,2,3,4])
print(list(f))
output:
[1, 2]

The main difference between a map and a filter is the return of values. A map will always have a representation for elements in the list. The filter will filter out the only elements that will meet the conditions in the function.
def checkElementIn(a):
nameList = ['b','a','l','l']
if a in nameList:
return a
testList = ['r','e','d','b','a','l','l']
m_list = map(checkElementIn,testList)
for i in m_list:
print(i)
None
None
None
b
a
l
l
f_list = filter(checkElementIn,testList)
for i in f_list:
print(i)
b
a
l
l

Those are completely different
just take a look at this clear example down below:
def sqr(x):
return x%2==0
mp = map(sqr, [-1,0,1,2,3,4,5,6])
print(list(mp))
[False, True, False, True, False, True, False, True]
fl = filter(sqr, [-1,0,1,2,3,4,5,6])
print(list(fl))
[0, 2, 4, 6]
as you can see in this clear example the filter doesn't care about the function results! It just checks which one of the list items would be true belonging to the calculation def, and the return is a list [0, 2, 4, 6] which means we have got a true result of numbers

Related

Function in Python list comprehension, don't eval twice

I'm composing a Python list from an input list run through a transforming function. I would like to include only those items in the output list for which the result isn't None. This works:
def transform(n):
# expensive irl, so don't execute twice
return None if n == 2 else n**2
a = [1, 2, 3]
lst = []
for n in a:
t = transform(n)
if t is not None:
lst.append(t)
print(lst)
[1, 9]
I have a hunch that this can be simplified with a comprehension. However, the straighforward solution
def transform(n):
return None if n == 2 else n**2
a = [1, 2, 3]
lst = [transform(n) for n in a if transform(n) is not None]
print(lst)
is no good since transform() is applied twice to each entry. Any way around this?
Use the := operator for python >=3.8.
lst = [t for n in a if (t:= transform(n)) is not None]
If not able/don't want to use walrus operator, one can use #functools.lru_cache to cache the result from calling the function and avoid calling it twice
import functools
eggs = [2, 4, 5, 3, 2]
#functools.lru_cache
def spam(foo):
print(foo) # to demonstrate each call
return None if foo % 2 else foo
print([spam(n) for n in eggs if spam(n) is not None])
output
2
4
5
3
[2, 4, 2]
Compared with walrus operator (currently accepted answer) this will be the better option if there are duplicate values in the input list, i.e. walrus operator will always run the function once per element in the input list. Note, you may combine finctools.lru_cache with walrus operator, e.g. for readability.
eggs = [2, 4, 5, 3, 2]
def spam(foo):
print(foo) # to demonstrate each call
return None if foo % 2 else foo
print([bar for n in eggs if (bar:=spam(n)) is not None])
output
2
4
5
3
2
[2, 4, 2]

Higher Order Function Using Filter for Prime Numbers and Odd Numbers Python

I want to make a Higher Order Function that will take 2 other functions, then a set of numbers, and return a set of numbers that went through that function. Sounds complicated but it's really not, here's my code:
#Is_Odd
def is_thodd(n):
if n % 3 == 1:
return True
else:
return False
#Is_Prime
def is_prime(n):
if n < 2:
return False
for f in range(2, int(math.sqrt(n)) + 1):
if n % f == 0:
return False
return True
def filter(f,x):
return [f(i) for i in x]
Here's what it shoudl do:
>>> A = [2, 3, 4, 5, 6, 7, 8, 9]
>>> filter(is_thodd, A)
[4, 7]
>>> filter(is_prime, A)
[2, 3, 5, 7]
>>> filter(is_prime, [])
[]
Here's what it does:
>>> A = [2, 3, 4, 5, 6, 7, 8, 9]
>>> filter(is_thodd, A)
[False, True, False, True, False, True, False, True]
I can't figure out how to fix it and need help. This is NOT a repeat of another question because it is fixing MY code, not how to do the problem. This is also not homework and is simply a way to better understand higher order functions.
Note that filter(function, iterable) is equivalent to the generator
expression (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. - built-in filter function docs
Since f(i) returns a boolean, your filter function implementation attempt will of course return a list of booleans instead. Instead you need to return the value of i only if f(i) is True like with the built-in function.
As mentioned by #rdas, your filter function should be this:
def filter(predicate, array):
return [item for item in array if predicate(item)]
I know this is just to understand higher order functions, but don't use this practically in a project because the filter function is built-in.

Python insert element based on condition

I'm trying to make a list where based on a condition, an element may or may not exist. For example, if it's true, the list is [1, 2, 3], and otherwise, it's [1, 3]. Currently, what I could do is either initialize the list and call .insert or .append the elements individually, or alternatively, I could do something like [1] + ([2] if condition else []) + [3], but that's ugly.
I was wondering if there was some sort of syntax like [1, 2 if condition, 3] for example, but I can't seem to find anything of that sort. Is there a similar syntax to this?
EDIT My list isn't [1, 2, 3]. I want a general solution for any type of object because I'm not even working with numbers (these are WTForms validators)
I'm also used to doing this in Perl with a pattern like:
my #arr = (1, (condition? (2) : ()), 3);
In Python you can get somewhat close to this with a solution that's pretty close to what you have with the list +, but uses * unpacking to avoid a lot of the other arrays:
arr = [1, *((2,) if condition else ()), 3]
Could clean that up a little with a helper that yields the value if the condition is true.
T = TypeVar("T")
def cond(val: T, ok: bool) -> Iterable[T]:
if ok:
yield val
arr = [1, *cond(2, condition), 3]
This has the downside of not short circuiting though, so if the creation of the used value is expensive you might want to provide a function instead of a value to be called and returned when the condition is true.
Yet another option could be to use a sentinel value and filter for it when constructing the list. Can combine this with a helper to do the filtering.
class _Ignore: pass
IGNORE = _Ignore()
def cond_list(*elements: Union[T, _Ignore]) -> list[T]:
return [e for e in elements if not isinstance(e, _Ignore)]
arr = cond_list(1, 2 if condition else IGNORE, 3)
If you want to stay flexible for the resulting container type you could choose to return an iterable which you can pass to a container constructor:
def cond_iter(*elements: Union[T, _Ignore]) -> Iterable[T]:
yield from (e for e in elements if not isinstance(e, _Ignore))
arr = set(cond_iter(1, 2 if condition else IGNORE, 3))
You can create a list with conditions and use list comprehension:
condition = [True, False, True, True, False]
[i for i in range(len(condition)) if condition[i]]
if you have a list of contidions for all elements you could to this:
elems = ['a', 'b', 'c']
conditions = [True, False, True]
lst = [item for item, condition in zip(elems, conditions) if condition]
print(lst)
that could also be done using itertools.compress:
from itertools import compress
elems = ['a', 'b', 'c']
conditions = [True, False, True]
lst = list(compress(elems, conditions))
or you generate your list and remove the element afterwards:
lst = ['a', 'b', 'c']
if condition:
lst.remove('b')
Is this what you're looking for?
[i for i in range(1,4) if i!=2]
UPDATE: based on nice answer above.
Making condition a function instead of a list will generalize it.
def condition(x):
if(x==2):
return False
else:
return True
[i for i in range(1,4) if condition(i)]
You should consider to generate the list with a generator like:
def generate(condition: bool, value: int):
yield 1
if condition:
yield value
yield 3
l = list(generate(True, 2)
You can use the splat/unpacking operator to expand an element either to the object you want to add or to nothing. Try changing the condition from True to False to check the results.
class FirstClass:
x = 5
class SecondClass:
y = 10
possible_object = (SecondClass().y,) if True else ()
l = [
FirstClass().x,
*possible_object,
FirstClass().x,
*possible_object,
]
print(l)
I think you got a variation of this answer before, but maybe this makes it more readable, like you wanted?
def is_not_dog(item):
return item != "dog"
items = ["cat", "dog", "horse", "snail"]
not_dog = [i for i in items if is_not_dog(i)]
not_dog
['cat', 'horse', 'snail']
Here's an attempt using filter():
conditioned = {2}
condition = True
[i for i in filter(lambda x: condition if x in conditioned else True, [1,2,3,4])]
# [1, 2, 3, 4]
condition = False
[i for i in filter(lambda x: condition if x in conditioned else True, [1,2,3,4])]
[1, 3, 4]
Of course you could replace the boolean condition with a function condition(), and use it as a filter instead of the lambda function.
If I have understood your question correctly, you want to output 1 predefined list if condition is true or return the same list plus an element if condition is false (for example). If that's correct you might want something like this:
a = [1, 2]
my_condition = 5 > 6
print(a if my_condition else a + [3])
Or if you want to define the list in the same line:
my_condition = 5 > 6
print([1,2] if my_condition else [1, 2] + [3])
In these cases 5 is not greater than 6 so the condition is false so [1, 2, 3] will be output.
Edit: just reading your question it looks like you might want a single value then for true + [one or more values] or for false + [one or more values] so this might be better:
a = [1]
print(a + [3] if my_condition else a + [2, 3])
print([1] + [3] if my_condition else [1] + [2, 3])
(Just using prints to demonstrate but you can assign this output to a variable using brackets)
I hope this answers your question.

How to make a python function that works as 'mapcar' of lisp

I want to know how can I make a python function that does the same as mapcar of lisp.
From the mapcar lisp documentation :
mapcar operates on successive elements of the lists. function is
applied to the first element of each list, then to the second element
of each list, and so on. The iteration terminates when the shortest
list runs out, and excess elements in other lists are ignored. The
value returned by mapcar is a list of the results of successive calls
to function.
For example,
list1 = [1, 2, 3, 4, 5]
list2 = [5, 4, 3, 2, 1]
def sum(firstNumber, secondNumber):
return firstNumber + secondNumber
sumOfLists = mapcar(sum, list1, list2)
print(sumOfLists)
# [6, 6, 6, 6, 6]
Use map, and also there is an operator for adding operator.add:
>>> import operator
>>> list(map(operator.add, list1, list2))
[6, 6, 6, 6, 6]
From the documentation. map takes a function as first argument, and a variable number of iterable arguments. The key is that the function should take as many arguments as iterables are given to map. That is the only "restriction" to take into account. So, for example:
map(lambda x: x+1, range(10))
map(lambda x, y: x+y, range(10), range(10))
map(lambda x, y, z: x+y+z, range(10), range(10), range(10))
And so on...
Also it can take any other function defined by the user:
def checkString(s):
return isinstance(s, str) and len(s) > 10
>>> list(map(checkString, ["foo", "fooooooooooooooooooooo"]))
[False, True]
This can be achieved in this way: sumOfLists = map(sum, zip(list1, list2))
You also do not need to define the sum function, as it is built-in.

Pair two lists in inverse order

What I want to do is to choose one item in list A and another one in list B, pair them like:
A[0]+B[n], A[1]+B[n-1],.....,A[n]+B[1]
I use two for loops but it doesn't work:
class Solution(object):
def plusOne(self, digits):
sum=0
for j in range(len(digits)-1,0,-1) :
for i in range(0,len(digits),1):
sum=sum+digits[i]*pow(10,j)
return sum+1
I inputted [1,2,3] and what I want to get is 124,
but I got 661.
Edit:
Sorry, the example I gave above is not so clear.
Let us think about A[1,2,3] and B[6,5,4].
I want output [5,7,9], because 5 is 1+4, 7 is 2+5, 9 is 3+6
What you are trying to do is turn a list of digits into the according number (and add 1). You can enumerate the reversed list in order to pair a digit with its appropriate power of 10:
digits = [1, 2, 3]
sum(10**i * y for i, y in enumerate(digits[::-1])) + 1
# 124
You can apply that to your other example as follows, using zip:
A = [1,2,3]
B = [6,5,4]
sum(10**i * (x+y) for i, (x, y) in enumerate(zip(B, A[::-1])))
# 579
You can do this without a loop:
A = [1,2,3]
B = [6,5,4]
C = list(map(sum,zip(A,B[::-1]) ))
print(C)
zip() - creates pairs of all elements of iterables, you feed it A and B reversed (via slicing). Then you sum up each pair and create a list from those sums.
map( function, iterable) - applies the function to each element of the iterable
zip() works when both list have the same length, else you would need to leverage itertools.zip_longest() with a defaultvalue of 0.
K = [1,2,3,4,5,6]
P = list(map(sum, zip_longest(K,C,fillvalue=0)))
print(P)
Output:
[5, 7, 9] # zip of 2 same length lists A and B reversed
[6, 9, 12, 4, 5, 6] # ziplongest for no matter what length lists
You only need one loop if you want to search in same list back and forth or different list with same length (i and len(lst)-1-i).
Try not use build-ins such as sum, list, tuple, str, int as variable names, it will give you some nasty result in some case.
class Solution(object):
def plusOne(self, digits):
sum_val = 0
for i in range(len(digits)):
sum_val += digits[i]*pow(10, len(digits)-1-i)
return sum_val+1
sol = Solution()
dig = [1, 2, 3]
print(sol.plusOne(dig))
Output:
124
for A = [1, 2, 3] and B = [6, 5, 4].
You can use a list comprehension:
res = [A[i]+B[len(A)-i-1] for i in range(len(A))]
Or the zip() function and a list comprehension:
res = [a+b for (a, b) in zip(A, reversed(B))]
Result:
[5, 7, 9]

Categories

Resources