Tuesday, January 09, 2007

Interesting Array Methods - Ruby on Rails

1. to_sentence (Array)

Use to join elements of an array into a more English-friendly representation.

>> %w[Chris Mark Steven].to_sentence
=> "Chris, Mark, and Steven"

You can pass it two options: :connector and skip_last_comma. They work like a’this:

>> %w[Soap Mocha Chocolate].to_sentence(:connector => '&')
=> "Soap, Mocha, & Chocolate"
>> %w[Ratatat Interpol Beirut].to_sentence(:skip_last_comma => true)
=> "Ratatat, Interpol and Beirut"

2. to_param (Array)

This guy, in case you’re unawares, is the method Rails calls on any object when trying to figure out how the object should be represented in a URL.

For instance, on an ActiveRecord::Base object:

>> user = User.find(:first)
=> #
>> helper.url_for :controller => 'users', :action => 'show', :id => user
=> "/users/1"

Now let’s say we override the to_param instance method on User to return name:

>> user = User.find(:first)
=> #
>> helper.url_for :controller => 'users', :action => 'show', :id => user
=> "/users/chris"

Cool. That said, the Array version of to_param joins elements with a /.

>> helper.url_for :controller => 'users', :action => 'show', :id => %w[one two three]
=> "/users/one%2Ftwo%2Fthree"

(That’s escaped, of course.) This is so you can easily send and deal with catch-all routes. This thread on RailsForum goes into catch-all routes. You may or may not find them useful.

3. to_s (Array)

Sure, you know Array has a to_s, but did you know Rails overrides it to accept a format (:db) parameter? That’s right! Check it out:

>> array_of_posts = Post.find(:all, :limit => 3)
=> [#, #, #]
>> array_of_posts.to_s(:db)
=> "1,2,3"
>> [].to_s
=> ""
>> [].to_s(:db)
=> "null"

This really only works with ActiveRecord objects, as Rails tries to call #id on the elements of the array. Unlike Time, this to_s currently doesn’t provide a hook for you to add more formats. One day? One day.

4. to_xml (Array)

Another more-or-less ActiveRecord specific extension, you may call to_xml on an array when all the elements of the array respond to to_xml. Like, you know, an array of ActiveRecord objects.

>> puts array_of_posts.to_xml
=> "


... tons of xml ...


Pass in :skip_instruct to omit the line. You can also provide an :indent option (default: 2), a :builder option (you probably want to stick with the default of Builder::XmlMarkup), a :root option (defaults to the lowercase plural name of the class, posts in this case), and a children option (singular lowercase class—post for me).

5. in_groups_of (Array)

The docs have this one covered pretty well: “Iterate over an array in groups of a certain size, padding any remaining slots with specified value (nil by default) unless it is false.

And, three examples. This is almost cheating. But here:

>> %w[1 2 3 4 5 6 7].in_groups_of(3) { |g| p g }
["1", "2", "3"]
["4", "5", "6"]
["7", nil, nil]

>> %w[1 2 3].in_groups_of(2, ' ') { |g| p g }
["1", "2"]
["3", " "]

>> %w[1 2 3].in_groups_of(2, false) { |g| p g }
["1", "2"]
["3"]

>> %w[1 2 3 4 5 6 7].in_groups_of(3)
=> [["1", "2", "3"], ["4", "5", "6"], ["7", nil, nil]]

6. split (Array)

Cuts up an array into smaller arrays in the same way String.split cuts up a string into arrays. Can take a parameter (a value to cut on) or a block (where the result is what’s used for the cuttin’).

>> %w[Tom Jerry and Mickey and Pluto].split('and')
=> [["Tom", "Jerry"], ["Mickey"], ["Pluto"]]
>> %w[Chris Mark Adam Tommy Martin Oliver].split { |name| name.first == 'M' }
=> [["Chris"], ["Adam", "Tommy"], ["Oliver"]]

5 comments:

siva said...

its really nice
but dont waste your blog space
just give short description and the url link.

Fernando said...

Hey this is incredible i hope this can helpme with a problem that dont letme work , when i send the params of my array to another controller give the id parameters in the next form:
1210/1343/1291
(the id's of my product that i choose)
THANKS!

Brian said...

siva, try to keep the comments positive! I found this really useful, thanks for posting it.

Especially the posts.to_s(:db)

Before I was doing:
posts.collect{|p|p.id}.join(',')

Thanks!
Brian

javakins said...

Hey Arun, I wasn't aware of to_sentence - that looks like quite a bit of fun. Thanks for posting!

domi said...

@siva: then how would it get indexed, and how would I have found this page?