I’m downloading Google Chrome as I write this. More later.
Welcome to the eighteenth installment of Enumerating Enumerable!
In this series of articles, I’m going through the methods in Ruby’s Enumerable
in alphabetical order, explaining what each does and providing examples. This is my attempt to make better documentation for Ruby’s Enumerable
module than Ruby-Doc.org’s.
In this article, I cover the include?
method.
In case you missed any of the previous articles, they’re listed and linked below:
- all?
- any?
- collect / map
- count
- cycle
- detect / find
- drop
- drop_while
- each_cons
- each_slice
- each_with_index
- entries / to_a
- find_all / select
- find_index
- first
- grep
- group_by
Enumerable#include? Quick Summary
In the simplest possible terms | Does the collection contain an item equal to this one? |
---|---|
Ruby version | 1.8 and 1.9 |
Expects | An argument containing the item to search for in the collection. |
Returns |
|
RubyDoc.org’s entry | Enumerable#include? |
Enumerable#include? and Arrays
When used on an array, include?
iterates through it, comparing items in the array with the argument using the ==
operator. If any of these comparisons has a result of true
, include?
returns true
. If you think of arrays as sets, you can think of include?
as a set membership test.
Examples
# Here's a list of words (if they seem unfamiliar, go read # http://en.wikipedia.org/wiki/Zoom_schwartz_profigliano) words = ["zoom", "schwartz", "profigliano", "butterman"] => ["zoom", "schwartz", "profigliano", "butterman"] # Is "profigliano" in our list of words? words.include? "profigliano" => true # How about "kwyjibo"? words.include? "kwyjibo" => false
Enumerable#include? and Hashes
include?
, when used with a hash, behaves differently than you might expect. You might think that include?
would return true
if given a two-element array argument that matched an item in the hash, where the first element matched the key and the second element matched the corresponding value.
However, that is not the case. Instead, include?
returns true
if there is a key in the hash that is equivalent to the given argument when compared with the ==
operator. In other words, include?
, when used with a hash, answers the question “Is there an item in this hash with this key?”
Examples
values = {"zoom" => 1, "schwartz" => 5, "profigliano" => 10, "butterman" => 25} => {"zoom"=>1, "schwartz"=>5, "profigliano"=>10, "butterman"=>25} values.include? "schwartz" => true values.include? ["schwartz", 5] => false values.include? "kwyjibo" => false
Once again, it’s Enumerating Enumerable time! This is the latest in my series of articles where I set out to make better documentation for Ruby’s Enumerable
module than Ruby-Doc.org’s. In this installment — the seventeenth in the series — I cover the group_by
method.
In case you missed any of the previous articles, they’re listed and linked below:
- all?
- any?
- collect / map
- count
- cycle
- detect / find
- drop
- drop_while
- each_cons
- each_slice
- each_with_index
- entries / to_a
- find_all / select
- find_index
- first
- grep
Enumerable#group_by Quick Summary
In the simplest possible terms | Break a collection into groups based on some given criteria. |
---|---|
Ruby version | 1.9 only |
Expects | A block containing the criteria by which the items in the collection will be grouped. |
Returns | A hash where each key represents a group. Each key’s corresponding value is an array containing the members of that group. |
RubyDoc.org’s entry | Enumerable#group_by |
Enumerable#group_by and Arrays
When used on an array, group_by
iterates through the array, passing each element to to the block. The result value of the block is the group into which the element will be placed.
Example 1
For the first example, I’ll use some code similar to the example given in Ruby-doc.org’s writeup of group_by
:
(0..15).group_by {|number| number % 3} => {0=>[0, 3, 6, 9, 12, 15], 1=>[1, 4, 7, 10, 13], 2=>[2, 5, 8, 11, 14]}
In the code above, the numbers 0 through 15 are passed to the block, which receives each number as the parameter number
. The group that each number is placed into is determined by the result value of the block, number % 3
, whose result can be one of 0, 1 or 2. This means that:
- The resulting hash will have three groups, represented by the keys
0
,1
and2
- The key
0
‘s corresponding value is an array containing the numbers in the range (0..15) that are evenly divisible by 3 (i.e. the numbers for whichnumber % 3
is 0. - The key
1
‘s corresponding value is an array containing the numbers in the range (0..15) that when divided by 3 leave a remainder of 1 (i.e. the numbers for whichnumber % 3
is 1. - The key
2
‘s corresponding value is an array containing the numbers in the range (0..15) that when divided by 3 leave a remainder of 2 (i.e. the numbers for whichnumber % 3
is 2.
Example 2
In the first example, the keys in the resulting hash are the same type as the values in the array whose contents we’re grouping. In this example, I’ll show that the keys in the resulting hash don’t have to be the same type as the values in the array.
simpsons = %w(Homer Marge Bart Lisa Abraham Herb) => ["Homer", "Marge", "Bart", "Lisa", "Abraham", "Herb"] simpsons.group_by{|simpson| simpson.length} => {5=>["Homer", "Marge"], 4=>["Bart", "Lisa", "Herb"], 7=>["Abraham"]}
In the code above, each Simpson name is passed to the block, which receives it as the parameter simpson
. The block’s result is the length of simpson
, and this result is the group into which the name will go.
In the resulting hash:
- Note that the keys are integers while the names in the groups are strings.
- The key
5
‘s array contains those names inSimpsons
that are 5
characters in length. - The key
4
‘s array contains those names inSimpsons
that are 4 characters in length. - The key
7
‘s array contains those names inSimpsons
that are 7 characters in length.
Example 3
In the previous two examples, the keys for the resulting array were calculated from the values in the initial array. In this example, I’ll demonstrate that the keys for the groupings can be determined in a completely arbitrary fashion that has nothing to do with the values:
# Put the Simpsons into randomly determined groups simpsons.group_by{rand(3) + 1} => {3=>["Homer", "Bart", "Abraham", "Herb"], 1=>["Marge", "Lisa"]} # Let's try that again. The results are very likely to be different: simpsons.group_by{rand(3) + 1} => {1=>["Homer", "Bart"], 2=>["Marge", "Lisa", "Herb"], 3=>["Abraham"]} # One more time! simpsons.group_by{rand(3) + 1} => {2=>["Homer", "Bart", "Lisa"], 3=>["Marge", "Herb"], 1=>["Abraham"]}
Enumerable#group_by and Hashes
When used on a hash, group_by
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.
Example 1
In this example, we’ll group the cast of Family Guy by the item that they’re bringing to a potluck dinner:
potluck = {"Peter" => "lasagna", "Lois" => "potato salad", "Chris" => "lasagna", "Meg" => "brownies", "Stewie" => "chateaubriand", "Brian" => "potato salad", "Evil Monkey" => "potato salad"} => {"Peter"=>"lasagna", "Lois"=>"potato salad", "Chris"=>"lasagna", "Meg"=>"brownies", "Stewie"=>"chateaubriand", "Brian"=>"potato salad", "Evil Monkey"=>"potato salad"} # Here's one way to do it: potluck.group_by{|person, bringing| bringing} => {"lasagna"=>[["Peter", "lasagna"], ["Chris", "lasagna"]], "potato salad"=>[["Lois", "potato salad"], ["Brian", "potato salad"], ["Evil Monkey", "potato salad"]], "brownies"=>[["Meg", "brownies"]], "chateaubriand"=>[["Stewie", "chateaubriand"]]} # Here's another way to do it: potluck.group_by{|person| person[1]} => {"lasagna"=>[["Peter", "lasagna"], ["Chris", "lasagna"]], "potato salad"=>[["Lois", "potato salad"], ["Brian", "potato salad"], ["Evil Monkey", "potato salad"]], "brownies"=>[["Meg", "brownies"]], "chateaubriand"=>[["Stewie", "chateaubriand"]]}
Example 2
In the previous example, the groupings were based on a calculation performed on the objects in the original hash. In this example, the groupings will be random: a random number generator will determine whose car each potluck attendee will ride to the potluck dinner:
potluck.group_by {[:peters_car, :quagmires_car, :clevelands_car][rand(3)]} => {:peters_car=>[["Peter", "lasagna"], ["Chris", "lasagna"], ["Evil Monkey", "potato salad"]], :quagmires_car=>[["Lois", "potato salad"], ["Meg", "brownies"], ["Stewie", "chateaubriand"]], :clevelands_car=>[["Brian", "potato salad"]]} # Let's try another random grouping potluck.group_by {[:peters_car, :quagmires_car, :clevelands_car][rand(3)]} => {:peters_car=>[["Peter", "lasagna"], ["Meg", "brownies"]], :quagmires_car=>[["Lois", "potato salad"], ["Stewie", "chateaubriand"], ["Brian", "potato salad"], ["Evil Monkey", "potato salad"]], :clevelands_car=>[["Chris", "lasagna"]]} # One more time! potluck.group_by {[:peters_car, :quagmires_car, :clevelands_car][rand(3)]} => {:peters_car=>[["Peter", "lasagna"], ["Chris", "lasagna"], ["Stewie", "chateaubriand"]], :quagmires_car=>[["Lois", "potato salad"], ["Evil Monkey", "potato salad"]], :clevelands_car=>[["Meg", "brownies"], ["Brian", "potato salad"]]}
Found these photos while digging through my archives on the backup hard drive and thought you folks might find them interesting. They’re flipchart pages from either June 2000, back when I was working at Cory Doctorow’s startup, OpenCola. These are notes that I wrote on the GNUtella protocol; Chris Cummer and I worked off these notes to produce an app called COLAvision, which scored Gnutellanet for audio and video files, captured them and cued them up to be served as streams (oh, the assignments we got during the bubble…).
I expect to be doing a fair bit of doodling, since the b5media tech office walls are largely floor-to-ceiling whiteboards and I’ve also been diving into Dan Roam’s book, The Back of the Napkin, a book on solving problems and selling ideas with hand-drawn pictures on whiteboards, flipcharts, notepads, scrap paper or even — as the title implies — the back of a napkin (or serviette to you Brits out there).
My notes on the GNUtella protocol, part 1.
My notes on the GNUtella protocol, part 2.
Pair Programming Chairs
The development tool company Cenqua have a cute gag ad for a pair programming chair called the “PairOn” — an Aeron built for two:
They list the PairOn’s “key features” as:
- Fully unit-tested in our ego-free ergonomics lab
- Essential office furniture for any eXtreme XP Pair (XXPP)
- Fully adjustable via individual or pair control
- Can be levered to standup-meeting height
- 40-hour-week alarm buzzer built in
- Available in a range of attractive colours
Here’s a pair programming chair that really exists: it’s a set of drawers that doubles as a stool:
These are drawer sets mounted on some very good wheels and topped with well-padded upholstery. If you wanted to pair program with someone, you’d simply pull the drawer unit out from under their desk and sit on it. These things were surprisingly comfortable; I’ve spent upwards of two hours sitting on one of these with nary a complaint.
Alas, these aren’t available in stores. They were custom-built for a startup for whom I briefly worked, whose CEOs was more in love with the idea of running a start-up than actually running a start-up. Their last programmer recently ditched them, and I’m hoping to pick up one of these in their bankruptcy sale.
Last night, I attended a special sneak preview for Internet Explorer 8 Beta 2 organized by the folks at High Road Communications, who do the PR for Microsoft here in Toronto. Pete LePage, Product Manager of Internet Explorer Developer Division, did the presentation, and also present were Elliot Katz, Senior Product Manager for Microsoft Canada, Daniel Shapiro, Microsoft Canada’s Audience Manager, and my friend and fellow DemoCamp steward David Crow, Tech Evangelist for Microsoft Canada.
Let me get the disclosure part out of the way. Attending this event got me:
- Free drinks and snacks during the presentation and a free dinner afterwards,
- One Internet Explorer 8 gym water bottle with a tag inside it saying “BPA Free”,
- and one 1GB USB key containing installers for IE8 (pictured in my laptop above) and the IE8 Evaluators’ Guide (a Word document that walks you through IE8’s features).
I’ve been to a couple of these Microsoft events before. The one about their “Windows Live” sites didn’t interest me at all, and the Vista one I attended was largely for people who did IT at companies with 1000 or more employees, which really isn’t my area of interest either (and the Vista preview installer they gave me resulted in disaster). This one was a considerably more interesting, as Pete put on a good presentation and it appears that Microsoft is making an effort to match the competing browsers.
Over the next little while, I’ll post articles covering my experiences as I take IE8 for a spin. In this article, I’ll mostly be talking about InPrivate Browsing, which is colloquially known as “Porn Mode”.
“Porn Mode”, a.k.a. “InPrivate Browsing”
The implementation of a browser session in which history, cache and other “trails of breadcrumbs” are deleted as soon as the session is over isn’t new: Apple’s Safari has a “Private Browsing” feature and there’s a Firefox extension that provides the same utility. However, for those not using Macs and especially those who aren’t the type to download and install Firefox and then install a plugin — and there are lots of these people out there — IE8 may be their first opportunity to try out such a feature.
Banking, Not Wanking
In his presentation, Pete was careful to take the “Banking, not wanking” approach when covering InPrivate Browsing, suggesting all sorts of non-saucy uses for the feature, including doing online banking, shopping for surprise presents for your spouse, surfing from a public terminal and so on. The Microsoft people present took my constant referring to it as “Porn Mode” in great stride, and I thank them for having a sense of humor about it.
The Problem
Convenience features like history, cache, automatic username and password field-filling are handy, but they sometimes have unintended consequences. For instance, suppose you, as a healthy, open-minded adult, like to look at videos featuring ladies without pants sitting on cakes at YouPorn.com. Let’s also suppose that a friend asks to borrow your computer for a moment to see a funny cat video at YouTube.com. As your friend types in the letters for “YouTube.com” in the address bar, this happens:
This sort of browser-assisted embarrassment takes place more often than you might think. I’ve seen it happen firsthand, and it’s done everything from causing a little red-facedness to actually thwarting romantic possibilities. And you thought computers were supposed to make our lives easier!
The IE8 solution, InPrivate Browsing, is accessible through the Safety menu (shown below) or through the control-shift-P key combo:
This opens up a new, separate browser window for InPrivate Browsing, which does not keep “breadcrumbs” like history, cache data, cookies and so on. The address bar for InPrivate Browsing windows has the InPrivate logo as a visual cue that this particular session won’t leave a trail that will embarrass you or give away your secrets:
Maybe it’s me, but I think the “InPrivate” graphic in the address bar is a bit too subtle. Then again, a more obvious visual indicator (say, giving the InPrivate browser window a different color) might be an invitation to shoulder-surf.
Hey man, I had to see if it works, right?
I swear, I had to poke about the site a little bit in order to test if my History was being saved. It’s all in the name of application testing!
After a little “research”, I closed not just the InPrivate Browsing window, but the whole browser, then started it up again. Then I proceeded to type “You” into the address bar. Under normal circumstances, my YouPorn.com history would be there for all to see. But it wasn’t!
For those of you who need to clear the cache, cookies, history or other data for any reason, there’s also the Delete Browsing History item in the Safety menu:
And it provides a number of deletion options:
And there you have it: a quick tour of IE8’s much-snickered-about “Porn Mode”.
Keep watching the blog for more posts about IE8 as I use it more and cover its features. Perhaps I’ll cover the development tools next.
This one’s for the Samsung Omnia (a.k.a. the i900):