.reduce: a versatile array method
Tags: ruby, beginners
Published: 2020-10-18 22:54:15

**.reduce** is a confusing enumerable.  But I'd like to think that what makes it confusing can also make it a powerful tool.

Today I'd like to dispel some of the confusion, and also show some examples of _versatile_ of a method it is. 

## What does .reduce do? 

Let's start with an easy example: Let's say we have an array of numbers that we want to _reduce_ down to a single value, it's sum. (Let's also say a powerful, Ruby-based wizard took away our **.sum** method)

```ruby
array = [1, 1, 1]

array.reduce(0) {|sum, i| sum + i} #=> 3
```

OK. Let's unpack what the code just did, above.

### **.reduce** as an _accumulator_ method

Above, we gave **.reduce** a single parameter (`reduce(0)`), as well as named two variables within the pipes (`|sum, i|`). Let's call the parameter the **start** value, and the two variables the **memo**, and **current_element**, respectively.

```ruby
array.reduce(start) {|memo, current_element| statement}
```

**current_element** is fairly straightforward; it is the current value of the array you are iterating over.

**memo** stores the return value of the previous iteration.

**start** provides an optional starting-point, by passing it's value as the first value of **memo**.

Let's look at the above example again, but this time at _each iteration_ that is happening: 

```ruby
array = [1, 1, 1]

array.reduce(0) {|sum, i| sum + i}

# 1st: 0 + 1 #=> 1
# 2nd: 1 + 1 #=> 2
# 3rd: 2 + 1 #=> 3
```

Each iteration is adding the **sum** value to the **current_element**, then saving that return as the next value for **sum**.  At the end of the iteration, the final value of **sum** is returned!

Before we move on... remember how I said that the parameter value was optional?  If we don't pass in the **start** value, the method will take `array[0]` for the first value of **memo**, and start iterating with `array[1]`!

```ruby
array = [1, 1, 1]

array.reduce {|sum, i| sum + i} #=> 3
```

Now that we understand the basics, let's get into some of the fun _versatility_ that **.reduce** has to offer.

## Using conditional logic to control our returns

Well, I guess I forgot my repellent.  The pesky wizard came back and this time he took our _max_by_ method...  How am I supposed to find the largest name out of this group of names?

```ruby
names = ["Gandalf", "Saruman", "Radagast"]
```

Well let's see what **.reduce** can do!

```ruby
names.reduce do |longest, current|
    if current.length > longest.length
        current
    else
        longest
    end
end #=> 'Radagast'
```

By implementing some control-flow, we can look at each value of the `names` array, and either return the `current` if it's longer than the `longest`, or keep the same value of `longest`for the next iteration.

This could be written more elegantly as well:

```ruby
names.reduce do |longest, current|
    current.length > longest.length ? current : longest
end #=> 'Radagast'
```
Ternary operators make up some of the bread-and-butter of **.reduce** control flow.

There is a glaring flaw in our method, however.  What happens when two or more names are of the same length?

```ruby
names = ["Bronson", "Dave", "Swanson", "Steve", "Johnson"]
```

Our current **.reduce** method will only return the _first_ longest name, since no subsequent values will pass `true` for `current.length > longest.length`!

## Returning multiple values with **.reduce**

Remember how we can pass an optional **start** parameter to our method?  Well we can actually use this to our advantage to create a **.reduce** method that returns an array of multiple responses!

```ruby
names = ["Bronson", "Dave", "Swanson", "Steve", "Johnson"]

names.reduce([""]) do |long_names, current|
    if current.length > long_names[0].length
        [current]
    elsif current.length == long_names[0].length
        long_names << current
    else
        long_names
    end
end #=> ["Bronson", "Swanson", "Johnson"]
```

To break this down, we need to keep the output consistent across each iteration, so since we want an array of strings, we put an array with an empty string as the **start** value.  After each iteration, we can either start a new array if `current` is greater than the previous longest name, we can add to the array if `current` is the same length as the previous longest name, or we can move on, by returning the `long_names` array in it's current form.

**.reduce** is a powerful tool, so I hope this helped make it more clear how it works.  There's still power yet to be explored with this enumerable, so as a cliffhanger, I'll leave you with another way to refactor the _sum_ example from earlier:

Instead of `array.reduce {|sum, i| sum + i}`...

Why not try `array.reduce(:+)`?