Categories
Uncategorized

Enumerating Enumerable: Enumerable#count

Welcome to the fourth installment of Enumerating Enumerable, a series of articles in which I challenge myself to do a better job of documenting Ruby’s Enumerable module than RubyDoc.org does. In this article, I’ll cover Enumerable#count, one of the new methods added to Enumerable in Ruby 1.9.

In case you missed the earlier installments, they’re listed (and linked) below:

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

Enumerable#count Quick Summary

Graphic representation of the Enumberable#count method in Ruby

In the simplest possible terms How many items in the collection meet the given criteria?
Ruby version 1.9 only
Expects Either:

  • An argument to be matched against the items in the collection
  • A block containing an expression to test the items in the collection
Returns The number of items in the collection that meet the given criteria.
RubyDoc.org’s entry Enumerable#count

Enumerable#count and Arrays

When used on an array and an argument is provided, count returns the number of times the value of the argument appears in the array:

# How many instances of "zoom" are there in the array?
["zoom", "schwartz", "profigliano", "zoom"].count("zoom")
=> 2

# Prior to Ruby 1.9, you'd have to use this equivalent code:
["zoom", "schwartz", "profigliano", "zoom"].select {|word| word == "zoom"}.size
=> 2

When used on an array and a block is provided, count returns the number of items in the array for which the block returns true:

# How many members of "The Mosquitoes" (a Beatles-esque band that appeared on
# "Gilligan's Island") have names following the "B*ngo" format?
["Bingo", "Bango", "Bongo", "Irving"].count {|bandmate| bandmate =~ /B[a-z]ngo/}
=> 3

# Prior to Ruby 1.9, you'd have to use this equivalent code:
["Bingo", "Bango", "Bongo", "Irving"].select {|bandmate| bandmate =~ /B[a-z]ngo/}.size

RubyDoc.org says that when count is used on an array without an argument or a block, it simply returns the number of items in the array (which is what the length/size methods do). However, when I’ve tried it in irb and ruby, I got results like this:

[1, 2, 3, 4].count
=> #<Enumerable::Enumerator:0x189d784>

Enumerable#count and Hashes

As with arrays, when used on a hash and an argument is provided, count returns the number of times the value of the argument appears in the hash. The difference is that for the comparison, each key/value pair is treated as a two-element array, with the key being element 0 and the value being element 1.

# Here's a hash where the names of recent movies are keys
# and their metacritic.com ratings are the corresponding values.
movie_ratings = {"Get Smart" => 53, "Kung Fu Panda" => 88, "The Love Guru" => 15,
"Sex and the City" => 51, "Iron Man" => 93}
=> {"Get Smart"=>53, "Kung Fu Panda"=>88, "The Love Guru"=>15, "Sex and the City"=>51, "Iron Man"=>93}

# This statement will return a count of 0, since there is no item in movie_ratings
# that's just plain "Iron Man".
movie_ratings.count("Iron Man")
=> 0

# This statement will return a count of 1, since there is an item in movie_ratings
# with the key "Iron Man" and the corresponding value 93.
movie_ratings.count(["Iron Man", 93])
=> 1

# This statement will return a count of 0. There's an item in movie_ratings
# with the key "Iron Man", but its corresponding value is NOT 92.
movie_ratings.count(["Iron Man", 92])
=> 0

count is not useful when used with a hash and an argument. It will only ever return two values:

  • 1 if the argument is a two-element array and there is an item in the hash whose key matches element [0] of the array and whose value matches element [1] of the array.
  • 0 for all other cases.

When used with a hash and a block, count is more useful. count 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.

Each key/value pair is passed to the block and count returns the number of items in the hash for which the block returns true.

# Once again, a hash where the names of recent movies are keys
# and their metacritic.com ratings are the corresponding values.
movie_ratings = {"Get Smart" => 53, "Kung Fu Panda" => 88, "The Love Guru" => 15,
 "Sex and the City" => 51, "Iron Man" => 93}
=> {"Get Smart"=>53, "Kung Fu Panda"=>88, "The Love Guru"=>15, "Sex and the City"=>51, "Iron Man"=>93}

# How many movie titles in the collection start
# in the first half of the alphabet?
# (Using a one-parameter block)
movie_ratings.count {|movie| movie[0] <= "M"}
=> 3

# How many movie titles in the collection start
# in the first half of the alphabet?
# (This time using a two-parameter block)
movie_ratings.count {|title, rating| title <= "M"}
=> 3

# Here's how you'd do it in pre-1.9 Ruby:
movie_ratings.select {|movie| movie[0] <= "M"}.size
=> 3
# or...
movie_ratings.select {|title, rating| title <= "M"}.size
=> 3

# How many movies in the collection had a rating
# higher than 80?
# (Using a one-parameter block)
movie_ratings.count {|movie| movie[1] > 80}
=> 2

# How many movies in the collection had a rating
# higher than 80?
# (This time using a two-parameter block)
movie_ratings.count {|title, rating| rating > 80}
=> 2

# Here's how you'd do it in pre-1.9 Ruby:
movie_ratings.select {|title, rating| rating > 80}.size
=> 2


# How many movies in the collection have both:
# - A title starting in the second half of the alphabet?
# - A rating less than 50?
# (Using a one-parameter block)
movie_ratings.count {|movie| movie[0] >= "M" && movie[1] < 50}
=> 1

# How many movies in the collection have both:
# - A title starting in the second half of the alphabet?
# - A rating less than 50?
# (This time using a two-parameter block)
movie_ratings.count {|title, rating| title >= "M" && rating < 50}
=> 1

# Here's how you'd do it in pre-1.9 Ruby:
movie_ratings.select {|title, rating| title >= "M" && rating < 50}.size
=> 1

(You should probably skip The Love Guru completely, or at least until it gets aired on TV for free.)

Categories
Uncategorized

b5media: #5 on TechVibes’ Start-Up Canada Index

b5media and Start-Up Index Canada logos

b5media, where I hold the position of Nerd Wrangler, has the position on TechVibes’ Start-Up Index Canada for July 2008.

Here are the top ten entries in the Index:

Rank Site Alexa Compete Average City/Region
1 MetroLyrics 492 381 437 Vancouver
2 Suite101 2,350 491 1,421 Vancouver
3 AbeBooks 6,274 2,428 4,351 Victoria
4 Wikitravel 4,596 4,925 4,761 Montreal
5 b5media 7,071 4,080 5,576 Toronto
6 NowPublic 7,954 6,396 7,175 Vancouver
7 TravelPod 7,997 7,384 7,691 Ottawa
8 Weblo 11,647 10,809 11,228 Montreal
9 amung.us 5,914 20,427 13,171 Alberta
10 iBegin 28,861 13,086 20,974 Toronto

Greg Andrews has been compiling Start-Up Index lists at the tech news site TechVibes for the past half year for nine cities and regions in Canada, including Accordion City. They rank companies not on profit or market share, but on much easier to collect data: web traffic to their sites, based on data from Alexa and Compete. It’s not the best measure of the performance of a company, but it might be a decent indicator of mindshare. The Start-Up Index Canada for July 2008 is his first such index compiled for Canada as a whole.

To qualify for inclusion on the list, a company has to meet the following criteria:

  • Located in Canada
  • Less than 5 years old
  • Not a public company
  • Is a tech company; either hardware, software, web app/service, or mobile

Congrats to my fellow coworkers and b5 bloggers!

Bonus Useless Data

If you were to treat my personal blog, The Adventures of Accordion Guy in the 21st Century as a startup, its average score, based on its Alexa ranking of 54,924 and Compete ranking of 48770, would put it in 21st place on the Start-Up Canada Index.