---
title: 'Anti-Pattern: Iteratively Building a Collection'
teaser: 'How Ruby''s Enumerable module can turn lengthy, verbose iterations into clear
  abstractions.

  '
tags: web,ruby
author: Mike Burns
published_on: 2013-11-25
---

Ruby comes with many fantastic Enumerable methods, but the two most
useful ones come through Smalltalk via LISP: `#map` and `#inject`. What
follows are some lengthy method definitions followed by rewrites that
are not only more concise but also more clear in their intentions.

## Building an array

Requirement:

> As a user with a PGP key I want to see the list of key ids for all my
> signers so I can quickly import them from the keyserver.

The initial implementation is a little lengthy and overly explicit:

```ruby
def signer_key_ids
  result = []

  signers.each do |signer|
    result << signer.key_id
  end

  result
end
```

But a simple use of `#map` more clearly illuminates what this method
does:

```ruby
def signer_key_ids
  signers.map { |signer| signer.key_id }
end
```

## Building an array from multiple arrays

Another requirement comes in:

> As a user with a PGP key I want to see the list of all UIDs for all my
> signers so I can see their names and where they work.

We can write this in a structured way using `#each` and `#flatten`:

```ruby
def signer_uids
  result = []

  signers.each do |signer|
    result << signer.uids
  end

  result.flatten
end
```

But a `#map` makes it more clear. Note the use of `Symbol#to_proc` here:

```ruby
def signer_uids
  signers.map(&:uids).flatten
end
```

An `#inject` combined with `Array#+` removes the need to call `#flatten`
at the end:

```ruby
def signer_uids
  signers.inject([]) do |result, signer|
    result + signer.uids
  end
end
```

Though in this case using `#inject` is the long way; instead try
`Enumerable#flat_map`:

```ruby
def signer_uids
  signers.flat_map(&:uids)
end
```

## Build a hash from an array

Another requirement comes in from above:

> As a user with a PGP key I want to see a mapping of all key ids to
> their UIDs for each signer so I can build my own keyserver.

Well we need to build a hash, and we need to build it from each element
in an array. At least, that's one way to phrase it:

```ruby
def signer_keys_and_uids
  result = {}

  signers.each do |signer|
    result[signer.key_id] = signer.uids
  end

  result
end
```

But another way to phrase it is: given an empty hash, `#inject` a hash
from key id to UIDs for each element in the array of signers:

```ruby
def signer_keys_and_uids
  signers.inject({}) do |result, signer|
    result.merge(signer.key_id => signer.uids)
  end
end
```

## Build a Boolean from an array

One last requirement, they swear:

> As a user with a PGP key I want to confirm that all my signers are
> signed by me so I can always feel mutually complete.

With the hash above we were dealing with another Enumerable. Here it's a
Boolean, so let's try it the long way:

```ruby
def mutually_signed?
  result = true

  signers.each do |signer|
    result = result && signer.signed_by?(self)
  end

  result
end
```

Though, now that we've seen that, it looks a bit familiar:

```ruby
def mutually_signed?
  signers.inject(true) do |result, signer|
    result && signer.signed_by?(self)
  end
end
```

But if that's too obtuse, we can always think of it as an array of
Booleans that must all be true:

```ruby
def mutually_signed?
  signers.map(&:signed_by?).inject(:&)
end
```

As Rubyists we also know that we have [other fantastic
abstractions](http://ruby-doc.org/core-2.0.0/Enumerable.html) up
our `Enumerable` sleeve:

```ruby
def mutually_signed?
  signers.all?(&:signed_by?)
end
```

## What's next

To get a comfortable intuition with `#map`, `#inject`, and other
`Enumerable` methods, I recommend going outside of Ruby for a bit. Some
amazing books on the topic of functional programming are:

* [Learn You a Haskell for Great Good](http://learnyouahaskell.com/)
* [The Little Schemer](http://www.ccs.neu.edu/home/matthias/BTLS/)
* [Smalltalk Best Practice Patterns](http://www.amazon.com/Smalltalk-Best-Practice-Patterns-Kent-ebook/dp/B00BBDLIME/ref=tmm_kin_title_0)
