# 4.9. Idiom Filter¶

• filter(callable, *iterables)

• Select elements from sequence

• Generator (lazy evaluated)

• required callable - Function

• required iterables - 1 or many sequence or iterator objects

>>> def even(x):
...     return x % 2 == 0
>>>
>>> result = (x for x in range(0,5) if even(x))
>>> result = filter(even, range(0,5))


## 4.9.1. Not-a-Generator¶

>>> from inspect import isgeneratorfunction, isgenerator
>>>
>>>
>>> def even(x):
...     return x % 2 == 0
>>>
>>>
>>> isgeneratorfunction(filter)
False
>>>
>>> result = filter(even, [1,2,3])
>>> isgenerator(result)
False


## 4.9.2. Problem¶

Plain code:

>>> def even(x):
...     return x % 2 == 0
>>>
>>>
>>> DATA = [1, 2, 3, 4, 5, 6]
>>> result = []
>>>
>>> for x in DATA:
...     if even(x):
...         result.append(x)
>>>
>>> print(result)
[2, 4, 6]


Comprehension:

>>> def even(x):
...     return x % 2 == 0
>>>
>>>
>>> DATA = [1, 2, 3, 4, 5, 6]
>>> result = [x for x in DATA if even(x)]
>>>
>>> print(result)
[2, 4, 6]


## 4.9.3. Solution¶

>>> def even(x):
...     return x % 2 == 0
>>>
>>>
>>> DATA = [1, 2, 3, 4, 5, 6]
>>> result = filter(even, DATA)
>>>
>>> list(result)
[2, 4, 6]


## 4.9.4. Lazy Evaluation¶

>>> def even(x):
...     return x % 2 == 0
>>>
>>>
>>> DATA = [1, 2, 3, 4, 5, 6]
>>> result = filter(even, DATA)
>>>
>>> next(result)
2
>>> next(result)
4
>>> next(result)
6
>>> next(result)
Traceback (most recent call last):
StopIteration


## 4.9.5. Performance¶

>>> def even(x):
...     return x % 2 == 0
>>>
>>>
>>> data = [1, 2, 3, 4, 5, 6]

>>>
... %%timeit -r 1000 -n 1000
... result = [x for x in data if even(x)]
1.11 µs ± 139 ns per loop (mean ± std. dev. of 1000 runs, 1,000 loops each)

>>>
... %%timeit -r 1000 -n 1000
... result = list(filter(even, data))
921 ns ± 112 ns per loop (mean ± std. dev. of 1000 runs, 1,000 loops each)


## 4.9.6. Use Case - 0x01¶

>>> users = [
... ]

>>> def above40(user):
...     return user['age'] >= 40
>>>
>>> def under40(user):
...     return user['age'] < 40

>>> result = filter(above40, users)
>>> list(result)

>>> result = filter(under40, users)
>>> list(result)


## 4.9.7. Use Case - 0x02¶

>>> users = [
...     {'is_admin': False, 'name': 'Mark Watney'},
...     {'is_admin': True,  'name': 'Melissa Lewis'},
...     {'is_admin': False, 'name': 'Rick Martinez'},
...     {'is_admin': False, 'name': 'Alex Vogel'},
...     {'is_admin': True,  'name': 'Beth Johanssen'},
...     {'is_admin': False, 'name': 'Chris Beck'},
... ]
>>>
>>>
>>>
>>>
>>> list(result)


## 4.9.8. Use Case - 0x03¶

>>> users = [
...     'mwatney',
...     'mlewis',
...     'rmartinez',
...     'avogel',
...     'bjohanssen',
...     'cbeck',
... ]
>>>
...     'mlewis',
...     'bjohanssen',
... ]
>>>
>>>
>>>
>>>
>>> list(result)
['mlewis', 'bjohanssen']


## 4.9.9. Use Case - 0x04¶

>>> class User:
...     firstname: str
...     lastname: str
...     groups: list[str]
...
...     def __init__(self, firstname, lastname, groups):
...         self.firstname = firstname
...         self.lastname = lastname
...         self.groups = groups
...
...     def __repr__(self):
...         return f'{self.firstname}'
...
>>> DATABASE = [
...     User('Mark', 'Watney', groups=['user', 'staff']),
...     User('Melissa', 'Lewis', groups=['user', 'staff', 'admin']),
...     User('Rick', 'Martinez', groups=['user', 'staff']),
...     User('Alex', 'Vogel', groups=['user']),
...     User('Beth', 'Johanssen', groups=['user', 'staff', 'admin']),
...     User('Chris', 'Beck', groups=['user', 'staff']),
... ]

>>> def is_user(user: User) -> bool:
...     return 'user' in user.groups
>>>
>>> def is_staff(user: User) -> bool:
...     return 'staff' in user.groups
>>>
>>> def is_admin(user: User) -> bool:

>>> users = filter(is_user, DATABASE)
>>> staff = filter(is_staff, DATABASE)

>>> list(users)
[Mark, Melissa, Rick, Alex, Beth, Chris]
>>>
>>> list(staff)
[Mark, Melissa, Rick, Beth, Chris]
>>>
[Melissa, Beth]


## 4.9.10. Assignments¶

"""
* Assignment: Idiom Filter Apply
* Required: yes
* Complexity: easy
* Lines of code: 3 lines
* Time: 3 min

English:
1. Define function odd():
a. takes one argument
b. returns True if argument is odd
c. returns False if argument is even
2. Use filter() to apply function odd() to DATA
3. Define result: filter with result
4. Run doctests - all must succeed

Polish:
1. Zdefiniuj funckję odd():
a. przyjmuje jeden argument
b. zwraca True jeżeli argument jest nieparzysty
c. zwraca False jeżeli argument jest parzysty
2. Użyj filter() zaaplikować funkcję odd() do DATA
3. Zdefiniuj result: filter z wynikiem
4. Uruchom doctesty - wszystkie muszą się powieść

Hints:
* filter()

Tests:
>>> import sys; sys.tracebacklimit = 0
>>> from inspect import isfunction

>>> assert isfunction(odd), \
'Object odd must be a function'

>>> assert result is not Ellipsis, \
'Assign result to variable: result'

>>> assert type(result) is filter, \
'Variable result has invalid type, should be filter'

>>> result = list(result)
>>> assert type(result) is list, \
'Evaluated result has invalid type, should be list'

>>> assert all(type(x) is int for x in result), \
'All rows in result should be int'

>>> result
[1, 3, 5, 7, 9]
"""

DATA = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

# Returns if number is odd (modulo divisible by 2 without reminder)
# type: Callable[[int], int]
def odd(x):
...

# Cube numbers in DATA
# type: filter
result = ...