Posts Tagged ‘enumerable’

Ruby Enumerable Magic: Aggregates

December 10, 2010

  1. The Basics
  2. Unary Ampersand Operator
  3. Booleans
  4. Filters
  5. New Collections
  6. Aggregates
 

This final article in my series about the Enumerable module details the methods that don’t quite fit elsewhere, but deal with the collection as a whole. As such, they each return just one object, not an array.

inject

If you haven’t used this method before, it’s loads of fun. It’s a cumulative method that uses each item in the collection to form a “final answer”. The classic example is finding the sum of the numbers in an array:

irb(main):001:0> [1, 2, 3].inject{|sum, num| sum += num}
=> 6

Another use might be finding the initials in a name:

irb(main):008:0> ['Jaime', 'Lee', 'Bellmyer'].inject(''){|initials, name| initials += name[0,1]}
=> "JLB"

In this example, I had to pass the starting string (”, or blank) to inject. Otherwise, it takes the first element as a whole, and starts adding onto it. I don’t like this behavior, and I suspect it works this way because inject assumes you’re trying to sum elements in some way. You also need to pass an initial value to inject when you want the result to be a different class than the inputs:

irb(main):009:0> [1, 2, 3].inject(0.0){|sum, i| sum += i.to_f}
=> 6.0

min and max

These methods behave largely like you’d expect. The items in the collection have to have the <=> method defined, just like the sorting methods, since finding the min and max requires sorting. So strings and numbers behave like you’d expect:

irb(main):010:0> ['joshua', 'gabriel', 'jacob'].min
=> "gabriel"
irb(main):011:0> ['joshua', 'gabriel', 'jacob'].max
=> "joshua"
irb(main):012:0> [1,2,3].min
=> 1
irb(main):013:0> [1,2,3].max
=> 3

And you can also pass your own custom sorting block, like you can with the sort method:

irb(main):014:0> ['joshua', 'gabriel', 'jacob'].min{|a,b| a.reverse <=> b.reverse}
=> "joshua"
irb(main):015:0> ['joshua', 'gabriel', 'jacob'].max{|a,b| a.reverse <=> b.reverse}
=> "gabriel"
irb(main):016:0> [1,2,3].min{|a,b| a*(-1) <=> b*(-1)}
=> 3
irb(main):017:0> [1,2,3].max{|a,b| a*(-1) <=> b*(-1)}
=> 1

In conclusion

What a long journey it has been. I’d like to thank myself for completing my longest series of articles, in the most timely manner yet. And I’d like to thank you if you suffered through all of it for the sake of knowledge. Please feel free to ask any questions you might have, and I’ll do my best to answer them promptly.

And thus concludes our two-week look at the Enumerable module.

Ruby Enumerable Magic: New Collections

December 8, 2010

  1. The Basics
  2. Unary Ampersand Operator
  3. Booleans
  4. Filters
  5. New Collections
  6. Aggregates
 

The Enumerable module offers several methods to create a new collection out of an existing one, by applying code to each item in the original collection.

map

This is the most common and straightforward method in the Enumerable module. It takes the original collection, applies the given block to each item within, and returns an array of the results:

irb(main):001:0> ['joshua', 'gabriel', 'jacob'].map{|name| name.capitalize}
=> ["Joshua", "Gabriel", "Jacob"]
irb(main):002:0> ['joshua', 'gabriel', 'jacob'].map(&:capitalize)
=> ["Joshua", "Gabriel", "Jacob"]

As with all Ruby methods that require a block, we can pass a block itself, or use the unary ampersand operator to pass a symbol that will be converted to a block and run on the object.

sort

This method will return the same, unaltered items in the collection, but in a sorted order. It does this using each item’s <=> method. Objects like strings choose to be sorted alphabetically. Number classes choose to sort numerically. The important thing is that the items in the collection must have this method defined.

Now for an example:

irb(main):003:0> ['joshua', 'gabriel', 'jacob'].sort
=> ["gabriel", "jacob", "joshua"]
irb(main):004:0> [3, 1, 5].sort
=> [1, 3, 5]

You can also pass a block that will be used to sort items, if the default sort is not good enough. For instance, we could choose to sort strings alphabetically, but starting from last letter to first:

irb(main):005:0> ['joshua', 'gabriel', 'jacob'].sort{|a,b| a.reverse <=> b.reverse}
=> ["joshua", "jacob", "gabriel"]

sort_by

A shorter way to sort based on the reversed version of strings like we did above, is to use the sort_by method, and passing either a block or using the unary ampersand operator to pass a symbol:

irb(main):007:0> ['joshua', 'gabriel', 'jacob'].sort_by{|name| name.reverse}=> ["joshua", "jacob", "gabriel"]
irb(main):008:0> ['joshua', 'gabriel', 'jacob'].sort_by(&:reverse)
=> ["joshua", "jacob", "gabriel"]

This is like calling map before calling sort. It will sort the results of the map method, not the collection items themselves.

zip

Honestly, I haven’t found a good use case for this method, but I also didn’t know it existed until I did the research for this article. It has nothing to do with compression – a better name for it might have been “zipper”. Have you ever noticed on the highway when two lanes merge, a zipper effect is created? People instinctively take turns getting in line, much like the teeth of a zipper.

This method works in a similar fashion:

irb(main):010:0> [1, 2, 3].zip([4, 5, 6])
=> [[1, 4], [2, 5], [3, 6]]
irb(main):011:0> [1, 2, 3].zip([4, 5, 6], [7, 8, 9])
=> [[1, 4, 7], [2, 5, 8], [3, 6, 9]]

It will take the original collection, and merge in the given array or arrays, as shown above. While I don’t yet know a great use case, I’ll keep my eyes peeled now that I understand how it works.

Ruby Enumerable Magic: Booleans

December 3, 2010

  1. The Basics
  2. Unary Ampersand Operator
  3. Booleans
  4. Filters
  5. New Collections
  6. Aggregates
 

The most basic Enumerable methods are those that give you a simple yes/no answer. There are three of these boolean methods: all?, any?, and include?. Each gives you quick info about the collection you’re working on.

We’ll start out our Team class from previous articles:

class Team
  include Enumerable
  
  attr_accessor :members
  
  def initialize
    @members = []
  end
  
  def each &block
    @members.each{|member| block.call(member)}
  end
end

all?

This method tells you if *every* item in your collection matches the criteria you give it. Let’s test it out:

irb(main):001:0> require 'team.rb'
=> true
irb(main):002:0> team = Team.new
=> #<Team:0x100391088 @members=[]>
irb(main):003:0> team.members = ['joshua', 'gabriel', 'jacob']
=> ["joshua", "gabriel", "jacob"]
irb(main):004:0> team.all?{|member| member.length > 4}
=> true
irb(main):005:0> team.all?{|member| member.length > 5}
=> false

Here we’re checking to see if all members (which are just strings containing their names) meet a certain length. All of the names have more than four characters, so all? returns true. However, only two of the three names contain more than five letters, so all? returns false in the second test.

Whatever block you pass to all? will be evaluated for each item in the collection (members in this case) and the method will only return true if all items return true.

any?

all? is a little strict, like an uptight parent demanding “all” your dirty clothes be put in the hamper. Wouldn’t it be nice to get partial credit? any? promotes permissive searches and parenting by only requiring that one item in the collection meet the given criteria. As long as any item evaluates to true (using the block you pass in), the whole collection gets the thumbs up.

irb(main):001:0> require 'team.rb'
=> true
irb(main):002:0> team = Team.new
=> #<Team:0x100391088 @members=[]>
irb(main):003:0> team.members = ['joshua', 'gabriel', 'jacob']
=> ["joshua", "gabriel", "jacob"]
irb(main):004:0> team.any?{|member| member == 'joshua'}
=> true
irb(main):005:0> team.any?{|member| member == 'javier'}
=> false

The example above is like a party. When you open the door and see three people standing there, you really only need to know *one* of them, and they’re all welcome. In the first example, if anybody in the team is named “joshua”, the whole collection is green-lighted for beer and awkward dancing. In the second example, if you don’t have a “javier” on your team, you’ll have to stay in the hallway, drinking the beer you brought yourself, and standing awkwardly.

include?

This is my favorite boolean enumerable method, simply because I always forget how to spell it. I always want to type includes? when the collection name is singular (like “team”). But it makes sense when the collection is plural, as in members.include?.

Our examples above can be shortened to use the include? syntax:

irb(main):001:0> require 'team.rb'
=> true
irb(main):002:0> team = Team.new
=> #<Team:0x100391088 @members=[]>
irb(main):003:0> team.members = ['joshua', 'gabriel', 'jacob']
=> ["joshua", "gabriel", "jacob"]
irb(main):004:0> team.include?('joshua')
=> true
irb(main):005:0> team.include?('javier')
=> false

First, note that include? doesn’t take a block – it takes one parameter, an item. This method will check the whole collection to see if that item is in there, and return true or false.

The Enumerable module contains a handful of handy boolean methods, but the real power comes in filtering collections down to the desired items. As luck would have it, that’s the topic of my next article.

Ruby Enumerable Magic: Unary Ampersand Operator

December 1, 2010

  1. The Basics
  2. Unary Ampersand Operator
  3. Booleans
  4. Filters
  5. New Collections
  6. Aggregates
 

You’ve probably seen this example in my previous post, or in other Ruby code:

irb(main):001:0> ['a', 'b', 'c'].map(&:capitalize)
=> ["A", "B", "C"]

This is a cool trick, which equates to:

['a', 'b', 'c'].map{|letter| letter.capitalize}

Most Ruby developers have seen this idiom, and even used it, but you may not know how it works. This is *not* strictly a piece of Enumerable magic, even though this is probably where you see it used most often. We can all harness this power, but much like the Force it can be used for good or evil.

There are really two pieces of magic used together to make this possible. The first is the unary ampersand operator. Contrary to what you might think, there is no “&:” operator in Ruby. What you’re seeing in my first example above is actually “&” and “:capitalize”, pushed together. That first character is the unary ampersand. The second piece is a Ruby symbol that is being passed to the operator.

When Ruby sees the unary ampersand on the last argument of a method, it tries to convert it to a proc (an executable block of code), and run it. It does this by calling to_proc on the object. This is where the second piece of magic comes in. Rails 1.1 added a to_proc method to the Symbol class, so that you could pass a symbol to the unary ampersand (ie, &:capitalize) and it would convert it for you. Ruby 1.8.7 rolled this change into Ruby itself, so you can use it outside of Rails.

Digging into “The Why” can be fun, but what I really wanted to know is how I could harness this Ruby voodoo (or, if you will, “rooboo”) for my own selfish gain. That was, by far, the hardest part of putting together this guide. The good news is, once I figured it out, it’s easy to pass on.

Let’s use the example of a Translator class with one public method: speak. This class has a method for each of the languages it knows, and a catch-all method (Ruby’s method_missing) for languages it doesn’t understand:

class Translator
  def speak &language
    language.call(self)
  end
  
  protected
  
  def french
    'bon jour'
  end
  
  def spanish
    'hola'
  end
  
  def turkey
    'gobble'
  end
  
  def method_missing *args
    'awkward silence'
  end
end

This is awesome! Let’s play with it in irb:

irb(main):001:0> require 'translator.rb'
=> true
irb(main):002:0> translator = Translator.new
=> #<Translator:0x10038ff08>
irb(main):003:0> translator.speak(&:spanish)
=> "hola"
irb(main):004:0> translator.speak(&:turkey)
=> "gobble"
irb(main):005:0> translator.speak(&:italian)
=> "awkward silence"

The key to this magic is in the speak method:

  def speak &language
    language.call(self)
  end

There’s a lot going on here. When Ruby converts :turkey into a proc from a symbol, it makes the assumption that a receiver will be passed into the proc, and the proc will be called *on* that receiver. In this case, we pass self into the proc above. This is like calling self.turkey. Gobble.

Is this a super useful example? Maybe not. But it lets you play with the smallest possible working example, and get comfortable. If you have any questions, please leave a comment below.

Ruby Enumerable Magic: The Basics

November 30, 2010

  1. The Basics
  2. Unary Ampersand Operator
  3. Booleans
  4. Filters
  5. New Collections
  6. Aggregates
 

You’re probably familiar with Ruby’s Enumerable module, even if you don’t know it by that name. It adds neat methods to arrays, like map, inject, select, and so on. You might have thought (like I did for a long time) that these methods were *array* methods, but they’re not.

Ruby’s arrays use Enumerable, and you can as well, in any class you want. Of course, the class should represent a collection of things, or all the iterative methods in Enumerable wouldn’t make much sense. Let’s say we have a Team class, that manages a group of members:

class Team
  include Enumerable
  
  attr_accessor :members
  
  def initialize
    @members = []
  end
  
  def each &block
    @members.each{|member| block.call(member)}
  end
end

Enumerable requires that your class contain an each method that serves up the items in the collection. All the other Enumerable methods rely on this. Now we can use the map method, for instance:

irb(main):001:0> require 'team.rb'
=> true
irb(main):002:0> team = Team.new
=> #<Team:0x100391088 @members=[]>
irb(main):003:0> team.members = ['joshua', 'gabriel', 'jacob']
=> ["joshua", "gabriel", "jacob"]
irb(main):004:0> team.map{|member| member.capitalize}
=> ["Joshua", "Gabriel", "Jacob"]

Now we can call any Enumerable methods on our team itself, and it will assume we want to work with the members array within. Enumerable can be a powerful mix-in to your own classes. We can even take this a step further, and clean up our call to map:

team.map(&:capitalize)

If you haven’t seen this before, it’s called the unary ampersand operator, and it’s the subject of my next post.


Follow

Get every new post delivered to your Inbox.