Write more semantic code with functional programming
Do you ever look at your code and see a waterfall of for loops? Do you find yourself having to squint your eyes and lean towards your monitor to get a closer look?
I know I do.
For loops are a Swiss army knife for problem-solving, but, when it comes to scanning code to get a quick read of what you’ve done, they can be overwhelming.
Three techniques — map, filter, and reduce — help remedy the for loop mania by offering functional alternatives that describe why you’re iterating. I previously wrote about getting started with these techniques in JavaScript, but the implementation is slightly different in Python.
We’ll briefly introduce each of the three techniques, highlight the syntactic differences between them in JavaScript and Python, and then give examples of how to convert common for loops.
What are Map, Filter, and Reduce?
Reviewing my previously written code, I realized that 95% of the time when looping through strings or arrays I do one of the following: map a sequence of statements to each value, filter values that meet specific criteria, or reduce the data set to a single aggregate value.
With that insight, these three methods are recognition — and implementation — that the reason you loop through an iterable often falls into one of these three functional categories:
- Map: Apply the same set of steps to each item, storing the result.
- Filter: Apply validation criteria, storing items that evaluate True.
- Reduce: Return a value that is passed from element to element.
What Makes Python Map/Filter/Reduce Different?
In Python, the three techniques exist as functions, rather than methods of the Array or String class. This means that instead of writing my_array.map(function)
you would write map(function, my_list)
.
Additionally, each technique will require a function to be passed, which will be executed for each item. Often, the function is written as an anonymous function (called a fat arrow function in JavaScript). However, in Python you often see lambda expressions being used.
The syntax between a lambda expression and arrow function is actually quite similar. Swap the =>
for a :
and make sure to use the keyword lambda
and the rest is almost identical.
// JavaScript Arrow Function const square = number => number * number;// Python Lambda Expression square = lambda number: number * number
One key difference between arrow functions and lambda expressions is that arrow functions are able to expand into full-blown functions with multiple statements while lambda expressions are limited to a single expression that is returned. Thus, when using map()
, filter()
, or reduce()
if you need to perform multiple operations on each item, define your function first then include it.
def inefficientSquare(number): result = number * number return resultmap(inefficientSquare, my_list)
Replacing For Loops
All right, on to the good stuff. Here are three examples of common for loops that will be replaced by map, filter, and reduce. Our programming prompt: Calculate the sum of the squared odd numbers in a list.
First, the example with basic for loops. Note: This is purely for demonstration and could be improved even without map/filter/reduce.
numbers = [1,2,3,4,5,6] odd_numbers = [] squared_odd_numbers = [] total = 0# filter for odd numbers for number in numbers: if number % 2 == 1: odd_numbers.append(number)# square all odd numbers for number in odd_numbers: squared_odd_numbers.append(number * number)# calculate total for number in squared_odd_numbers: total += number# calculate average
Let’s convert each step to one of the functions:
from functools import reducenumbers = [1,2,3,4,5,6]odd_numbers = filter(lambda n: n % 2 == 1, numbers)squared_odd_numbers = map(lambda n: n * n, odd_numbers)total = reduce(lambda acc, n: acc + n, squared_odd_numbers)
There are a few important points of syntax to highlight.
map()
andfilter()
are natively available. However,reduce()
must be imported from thefunctools
library in Python 3+.- The lambda expression is the first argument in all three functions while the iterable is the second argument
- The lambda expression for
reduce()
requires two arguments: the accumulator (the value that is passed to each element) and the individual element itself.
Source: medium