Categories
Uncategorized

Enumerating Ruby’s “Enumerable” Module, Part 3: “detect”, a.k.a. “find”

Enumerable#find: A magnifying glass focused on a ruby

Welcome to the third installment in my series of articles on the methods of Ruby’s Enumerable module. This series is meant to address some of the shortcomings in the official documentation

In case you missed the first two, here they are:

  1. all? and any?
  2. collect/map

In this installment, I’m going to cover the find method. This is a particularly interesting one because it covers detect/find‘s little-talked about optional parameter.

detect, a.k.a. find

  • In plain language: What’s the first item in the collection that meets the given criteria?
  • Ruby.Doc.org’s entry: Enumerable#detect / Enumerable#find
  • Expects:
    • A block containing the criteria.
    • An optional argument containing a proc that calculates a “default” value — that is, the value to return if no item in the collection matches the criteria.
  • Returns:
    • The first item in the collection that matches the criteria, if one exists.
    • If no such item exists in the collection:
      • nil if no argument is provided
      • The value of the argument if one is provided
  • detect and find are synonyms — you can use either. I personally prefer find, as it’s shorter and a good match with a related method, find_all. I also just think that “find” conveys the method’s functionality much better than “detect”.

    Using detect/find with Arrays

    When used on an array without an argument, detect/find passes each item from the collection to the block and…

    • If the current item causes the block to return a value that doesn’t evaluate to false, detect/find stops going through collection and returns the item.
    • If no item in the collection causes the block to return a value that doesn’t evaluate to false, detect/find returns nil.

    classic_rock_bands = ["AC/DC", "Black Sabbath", "Queen", "Scorpions"]
    
    classic_rock_bands.find {|band| band > "Led Zeppelin"}
    => "Queen"
    
    classic_rock_bands.find {|band| band > "ZZ Top"}
    => nil
    

    Using the optional argument is a topic big enough to merit its own section, which appears later in this article.

    Using detect/find with Hashes

    With hashes, detect/find passes each key/value pair in the hash to the block, which you can “catch” as either:

    1. A two-element array, with the key as element 0 and its corresponding value as element 1, or
    2. Two separate items, with the key as the first item and its corresponding value as the second item.

    detect/find is one of those methods of Enumerable that works a little oddly since:

    • The result it returns depends on the order of the collection
    • We’re always told that hashes don’t really any order (there seems to be one, but it’s shrouded in mystery).

    metacritic_ratings = {"Juno" => 81, "American Gangster" => 76, \
                          "Golden Compass" => 51, "Meet the Spartans" => 9}
    => {"American Gangster"=>76, "Golden Compass"=>51, "Juno"=>81, "Meet the Spartans"=>9}
    
    metacritic_ratings.find {|metacritic_rating| metacritic_rating[1] > 80}
    => ["Juno", 81]
    
    metacritic_ratings.find {|film, rating| rating > 80}
    => ["Juno", 81]
    
    metacritic_ratings.find {|film, rating| rating > 90}
    => nil
    

    Using detect/find with the Optional Argument

    detect/find‘s optional argument lets you specify a proc or lambda whose return value will be the result in cases where no object in the collection matches the criteria.

    (Unfortunately, a complete discussion of procs and lambdas is beyond the scope of this article. I highly recommend looking at Eli Bendersky’s very informative article, Understanding Ruby blocks, Procs and methods.)

    I think that the optional argument is best explained through examples…

    classic_rock_bands = ["AC/DC", "Black Sabbath", "Queen", "Scorpions"]
    
    # Let's define a proc that will simply return the default band's name
    # for cases where none of the bands in the array meets the criteria.
    default_band = Proc.new {"ABBA"}
    
    # Procs are objects, so using a proc's name alone isn't sufficient
    to invoke its code -- doing so will simply return the proc object.
    default_band
    => #<Proc:0x00553f34@(irb):31>
    # (The actual value will be different for you, but you get the idea.)
    
    # To call a proc, you have to use its "call" method:
    default_band.call
    => "ABBA"
    
    # detect/find calls the "call" method of the object you provide as the argument
    # if no item in the collection matches the criteria in the block.
    classic_rock_bands.find(default_band) {|band| band > "Led Zeppelin"}
    => "Queen"
    
    classic_rock_bands.find(default_band) {|band| band > "ZZ Top"}
    => "ABBA"
    
    # Let's try something a little fancier, and use a lambda this time.
    # The differences between procs and lambdas are very fine -- I suggest
    # you check Eli Bendersky's article for those differences.
    random_band = lambda do
    	fallback_bands = ["Britney Spears", "Christina Aguilera", "Ashlee Simpson"]
    	fallback_bands[rand(fallback_bands.size)]
    end
    
    # Let's give it a try...
    classic_rock_bands.find(random_band) {|band| band > "ZZ Top"}
    => "Britney Spears"
    >> classic_rock_bands.find(random_band) {|band| band > "ZZ Top"}
    => "Ashlee Simpson"
    >> classic_rock_bands.find(random_band) {|band| band > "ZZ Top"}
    => "Christina Aguilera"
    

    To see a “real” application of detect/find's optional argument, see this Ruby Quiz problem.

    Categories
    Uncategorized

    SEO Tips for Rails Apps

    Default Routes Considered Harmful, and Other Rails SEO Tips covers SEO for Rails apps, with both on-page SEO tips (Prettier URLs, Better Title Tags and DRY in Content) and off-page SEO tips (Easy Linking and Bookmarks).

    Categories
    Uncategorized

    10 Reasons to Learn and Use Regular Expressions

    10 Reasons to Learn and Use Regular Expressions: Yes, they’ll drive you crazy every now and again, but as the authors of Programming Perl wrote, “if you take ‘text’ in the widest possible sense, perhaps 90% of what you do is 90% text processing.”

    Categories
    Uncategorized

    Adding Type Checking in Ruby

    Here’s an article on adding type-checking to Ruby which pays a lot of attention to the Types framework.

    Categories
    Uncategorized

    How to Read C Declarations

    Here’s a handy guide to reading C declarations, where things can get very confusing very quickly. Quick — what’s being declared in the line “int (*(*vtable)[])();” ?

    Categories
    Uncategorized

    Your First Warning: DemoCamp 17 Takes Place on Monday, February 25th

    DemoCamp logo

    That’s right — DemoCamp, the gathering of the bright lights of Toronto’s tech community takes place in a couple of weeks! Here are the details:

    • When: Monday, February 25th, from 6:00 p.m. to 9:00 p.m. (Doors typically open around 5 – 5:30)
    • Where: Toronto Board of Trade, 1 First Canadian Place (King and Bay), Toronto, Ontario, Canada
    • Niceties include: Free food and a cash bar
    • Cost: Free, but you have to sign up, and there’s also the option of making a donation (see below)
    • After-party: After the presentations, we’ll adjourn to the nearby Duke of Westminster (77 Adelaide Street West)

    I’ll go into more detail about DemoCamp in a later posting, but in the meantime, here are some handy links to get you started:

    • We use this Eventbrite page for sign-ups. You need to sign up to attend, and there are three sign-up options:
      • You can “purchase” a free ticket, which as the name implies, means that it costs nothing
      • You can purchase a Community Supporter ticket for $5
      • You can purchase a Community All-Star ticket for $10 (I bought one just now)
    • You’ll find more details about the event at the DemoCamp 17 page on the BarCamp wiki.
    • If you want to do a demo or Ignite presentation at DemoCamp 17, sign up here.
    • DemoCamp sells sponsorships! Sponsorship means prominent placement of your logo during the opening comments, and a representative from your company or organization gets 2 minutes at the podium. Sponsorships go for a mere $200 — contact Jay Goldman for details.

    As I wrote earlier, I’ll post more details later.

    Categories
    Uncategorized

    A New ActiveMerchant Ebook

    ActiveMerchant payment authorization diagram

    [Via Ruby Inside] ActiveMerchant is a new PDF-format ebook covering the ActiveMerchant Ruby library for handling payments. It supports a number of payment gateways, including PayPal, Authorize.Net, and TrustCommerce. It’s a brief 74 pages long and sells for a dirt-cheap $9. I’m going to order myself a copy later today.