An Introduction to Python Higher Order Functions
Tags: python
Published: 2020-09-14 19:29:27

In a previous post, I created a guide for JavaScript higher-order functions to make dealing with array computations easier. In this blog post, I will do the same thing but with Python as the language. Hopefully with these examples, you'll find it easier to sort and manage lists in Python.

If you are interested in reading the JavaScript blog post, you may find it [here](https://dev.to/brandonbrown4792/javascript-higher-order-functions-a-short-and-sweet-reference-guide-4bjh). 

## All
The all method accepts an iterable as an argument and will return True only if all conditions of the iterable are True. Otherwise, the all method with return False.

```python
arr = [1, 2, 3, 4, 5]

all(i > 0 for i in arr)
=> True

all( i < 4 for i in arr)
=> False
```

## Filter
The filter method returns a new list of all elements of the list that satisfy a condition. This method accepts a function and an iterable as arguments. However, the filter method returns a filter object and not a list. Therefore, we have to convert the filter object into a list.

```python
arr = [1, 2, 3, 4, 5]

def filter_function(x):
  return x > 2

list(filter(filter_function, arr))
=> [3, 4, 5]

# Alternatively
list(filter(lambda x: x > 2, arr))
=> [3, 4, 5]
```

Although the previous filter  would work, it can be a little confusing since you must pass in a function as an argument. Python offers a great feature called list comprehension which is more widely used when filtering.

```python
arr = [1, 2, 3, 4 5]

[x for x in arr if x > 2]
=> [3, 4, 5]
```

Using list comprehension is considered to be more "Pythonic" and should be used primarily. However, if your filter requires a multi-step process, the filter function is probably your best bet.

## Next
The next method returns the first element of a list that satisfies a condition. This is similar to the find method in other languages. Next accepts an iterable as an argument. See the example below:

```python
arr = [1, 2, 3, 4, 5]

next(x for x in arr if x > 2)
=> 3
```

If Python cannot find the element in the list, a StopIteration exception will be thrown and could crash your program if not handled correctly. Because of this, Python allows you to specify a default parameter.

```python
arr = [1, 2, 3, 4, 5]

next(x for x in arr if x > 5)
=> StopIteration

next((x for x in arr if x > 5), None)
=> None
```

*Hint:* Do not forget to wrap your iterator in parentheses. Not doing so will cause a syntax error.

## Includes
Python checks if elements exist in an array by using the "in" keyword. See the example below:

```python
arr = [1, 2, 3, 4, 5]

1 in arr
=> True

6 in arr
=> False
```

## For Each
To perform an action on every element of an array, Python uses the for loop. See the example below:
```python
arr = [1, 2, 3, 4, 5]

for x in arr:
  print(f'Hello, I am {x}')
=> Hello, I am 1
Hello, I am 2
Hello, I am 3
Hello, I am 4
Hello, I am 5
```

It is important to note that changing the value of x does not change the original list. 

```python
arr = [1, 2, 3, 4, 5]

for x in arr:
  x = x * 2

print(arr)
=> [1, 2, 3, 4, 5]
```

There is a way to return an updated list; however, map (which we will talk about next) is a better option as opposed to the for loop. The for loop should primary be used to do something external for each item in the list.

## Map
The map method returns a new list with all elements transformed by a function. This method accepts two arguments: a function and an iterable. Similar to filter, the map method returns a map object, so we must convert this map object to a list. See the example below:

```python
arr = [1, 2, 3, 4, 5]
def square(x):
  return x * x

list(map(square, arr))
=> [1, 4, 9, 16, 25]

# Alternatively...
list(map(lambda x: x * x, arr))
```

However just like the filter method, you may use list comprehension to solve this problem. See the example below:

```python
arr = [1, 2, 3, 4, 5]

[x * x for x in arr]
=> [1, 4, 9, 16, 25]
```

Again, list comprehension is the preferred method and is considered more "Pythonic."

## Reduce
The reduce method *reduces* all values of a list into a single value. For example, the sum of a list can be considered as one of the simplest reducers since it takes all the elements of that list and returns a single value. This method is similar to filter and map in feel, but it is a little more complicated. The reduce method takes two required arguments and one optional argument. As its first required argument, the reduce method accepts a method that contains two arguments. The first argument of this inner function is the accumulator while the second is an element of the list. That is all for the inner method. The second argument of the reduce method is the list, and the third is an optional start parameter.

Additionally, the reduce function requires the Python library functools, so do not forget to import that library. I know this seems confusing, so hopefully the code will help you better understand.

```python
import functools

def square_reducer(x, y):
  return x * y

arr = [1, 2, 3, 4, 5]

functools.reduce(square_reducer, arr)
=> 120

# Alternatively
functools.reduce(lambda x, y: x * y, arr)
=> 120

functools.reduce(square_reducer, arr, 2)
=> 240

# Alternatively
functools.reduce(lambda x, y: x * y, arr, 2)
=> 240
```

The first reduce function is the most straightforward. First we go to the first element and place it into the accumulator (x). Then we multiply the accumulator by the second element. So 1 * 2 = 2. Our accumulator value is 2. Next, we multiply the accumulator value by the third element (2 * 3). So we get a value of 6. We go on and on until we finish going through the entire array. In the end, this is what is happening:

```python
1 * 2 * 3 * 4 * 5
=> 120
```

The second reduce function is doing the same exact thing but making use of a lambda function so we do not have to declare a reducer method separately.

The third function is doing the same as the first, however, it is starting with the accumulator set to 2. Therefore, this is the same as:

```python
2 * 1 * 2 * 3 * 4 * 5
=> 240
```

Again, the fourth is doing the same as the third but using a lambda function.

## Sort
The sort method returns a new sorted version of a list. The sorted method takes in one required argument and two optional arguments (reverse and key). The first argument is the list. The reverse optional parameter reverses the sorted list, and the key optional parameter is a function to base the list sort upon. See the examples below:

```python
arr = [2, 1, 5, 3, 4]

sorted(arr)
=> [1, 2, 3, 4, 5]

sorted(arr, reverse=True)
=> [5, 4, 3, 2, 1]

students = [
  {'name': 'Jimmy', 'age': 15},
  {'name': 'Hector', 'age': 18},
  {'name': 'Paige', 'age': 16}
]

sorted(students, key=lambda x: x['age'])
=> [{'name': 'Jimmy', 'age': 15}, {'name': 'Paige', 'age': 16}, {'name': 'Hector', 'age': 18}]
```

## Min
The min method searches a Python list for a minimum value. This method accepts one required parameter and three optional parameters. The required parameter is the iterable itself. Optionally, you may add more objects to this function as ongoing arguments. You may also sort using a key (similar to sorted method) and specify a default value. See the examples below:

```python
arr = [1, 2, 3, 4, 5]

min(arr)
=> 1

min(0, -2, 1, 8, 5)
=> -2

students = [
    {'name': 'Jimmy', 'age': 15},
    {'name': 'Hector', 'age': 18},
    {'name': 'Paige', 'age': 16}
]

min(students, key=lambda x: x['age'])
=> {'name': 'Jimmy', 'age': 15}

min([], default=0)
=> 0
```

## Max
The max method is identical to the min function; however, it finds the maximum value in an iterable. See the examples below:

```python
arr = [1, 2, 3, 4, 5]

max(arr)
=> 5

max(0, -2, 1, 8, 5)
=> 8

students = [
    {'name': 'Jimmy', 'age': 15},
    {'name': 'Hector', 'age': 18},
    {'name': 'Paige', 'age': 16}
]

max(students, key=lambda x: x['age'])
=> {'name': 'Hector', 'age': 18}

max([], default=100)
=> 100
```

## Conclusion
Higher order functions are wonderful to use and can save you a lot of time and headache if you are aware of them. I hope you find these methods helpful and easy to use. Happy coding!