Reduce: The Swiss Army Knife of Basic Functional Programming

crywolfe

Gerry Wolfe

Posted on April 21, 2023

Reduce: The Swiss Army Knife of Basic Functional Programming

Functional programming is a paradigm that emphasizes the use of pure functions, immutable data, and higher-order abstractions. One of the most common and powerful higher-order abstractions in functional programming is the reduce function.

The reduce function takes an iterable (array, list, or other collection) and a function that combines two values, and applies that function to all the elements of the iterable returning a single value (which could be a primitive or an object). The canonical example is summing up an array of numbers.

# Python code
numbers = [1, 2, 3, 4, 5]
sum = reduce(lambda x, y: x + y, numbers)
assert (sum == 15) # True
Enter fullscreen mode Exit fullscreen mode
// Typescript code
const numbers = [1, 2, 3, 4, 5];
const sum = numbers.reduce((x, y) => x + y);
assert (sum === 15); // true
Enter fullscreen mode Exit fullscreen mode

The power of the reduce function is its versatility. It can be used to implement many other iterable operations such as map, filter, find, etc.

For example, a filter function is just an implementation of reduce. Let’s see how we can use the reduce function to create our own filter function. A filter function takes an iterable and a predicate (a function that returns true or false), and returns a new object with only the elements that satisfy the predicate. To use the reduce function as a filter function, we need to define a filter function.

# Python code
def filter_by(acc, cur):
  # Check if the current element satisfies the condition using the boolean function
  if condition(cur):
    # If True, append the current element to the accumulator array
    acc.append(cur)

  # Return the accumulator list
  return acc
Enter fullscreen mode Exit fullscreen mode
// Typescript code
function filterBy(acc: any[], cur: any): any[] {
  // Check if the current element satisfies the condition using the boolean function
  if (condition(cur)) {
    // If true, push the current element to the accumulator array
    acc.push(cur);
  }

   // Return the accumulator array
   return acc;
}
Enter fullscreen mode Exit fullscreen mode

Now we can use this function with the reduce function to filter out the even numbers from an array of numbers.

# Python code
numbers = [1, 2, 3, 4, 5]

# Define a boolean function that checks if a number is odd
def condition(x):
  return x % 2 == 1

# Use reduce and filter_by to filter out the even numbers from numbers
odd_numbers = reduce(filter_by, numbers, [])

print(odd_numbers) # [1, 3, 5]
Enter fullscreen mode Exit fullscreen mode
// Typescript code
const numbers = [1, 2, 3, 4, 5];

// Define a boolean function that checks if a number is odd
function condition(x: number): boolean {
  return x % 2 == 1;
}

// Use reduce and filterBy to filter out the even numbers from numbers
const oddNumbers: number[] = numbers.reduce(filterBy, []);

console.log(oddNumbers); // [1,3,5]
Enter fullscreen mode Exit fullscreen mode

Now, let's use reduce to return a Record object. If you have an array of strings that represent colors, you can use a reduce function to count how many times each color appears in the array and return an object with the color names as keys and the counts as values.

# Import reduce from functools module
from functools import reduce

# Define a list of colors
colors = ["red", "blue", "green", "red", "yellow", "blue", "green", "red"]

def get_color_counts(colors):
  # Use reduce on the list
  return reduce(lambda counts, color: 
    # For each color, check if it already exists as a key in the counts dictionary
    counts.update({color: counts.get(color, 0) + 1}) or counts
    # If yes, increment its value by one
    # If no, initialize its value to one
    # Use update and get methods on the dictionary and return the updated counts dictionary
  , colors, {}) # Start with an empty dictionary for the counts

print(get_color_counts(colors)) # {'red': 3, 'blue': 2, 'green': 2, 'yellow': 1}
Enter fullscreen mode Exit fullscreen mode
// Typescript code

const colors: string[] = ["red", "blue", "green", "red", "yellow", "blue", "green", "red"];

function getColorCounts(colors: string[]): Record<string, number> {
  // Use the reduce method on the array
  return colors.reduce((counts: Record<string, number>, color: string) => {
    // For each color, check if it already exists as a key in the counts object
    if (counts[color]) {
      // If yes, increment its value by one
      counts[color] += 1;
    } else {
      // If no, initialize its value to one
      counts[color] = 1;
    }
    // Return the updated counts object
    return counts;
  }, {}); // Start with an empty object for the counts
}

console.log(getColorCounts(colors)); // {red: 3, blue: 2, green: 2, yellow: 1}
Enter fullscreen mode Exit fullscreen mode

Those are just a few examples of the powerful functional nature of a reduce function that allows for concise and expressive transformations of iterable data structures. It can be used to implement many other higher-order functions such as map, filter, and find. The reduce function can help simplify complex computations and make code more elegant and efficient.

💖 💪 🙅 🚩
crywolfe
Gerry Wolfe

Posted on April 21, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related