ids = holder.things.collect { |thing| thing.id }
(I prefer to say map
rather than collect
, but
they're really the same thing. Which one you use is largely a matter of
taste, influenced by what languages you've used in the past, and your laziness
in typing.)
There are two small successive improvements that can be made to this. First, when you have any code of the form:
bunch_of_things.collect { |thing| thing.some_method }
(and remember, retrieving a data-member of an object is a method!)
you can shorten that to:
bunch_of_things.collect(&:some_method)
This uses the &
shorthand for Ruby's to_proc
method. Long story short,
the :
makes a Symbol, and the &
calls
to_proc
on that. collect
will send that to each
item in turn, making it behave just like a block explicitly calling it on each
item. (I won't go into the nitty-gritty details here of how that
works; if you care, investigate Ruby's yield
keyword.)
For example, if you have a block of numbers and you want to get their even-ness, you can do:
[1, 2, 3, 5, 8].map(&:even?)
# => [false, true, false, false, true]
You can also use the &:
trick with block-taking
methods other than collect/map
, such as
inject/reduce
:
[1, 2, 3, 4, 5].inject(&:+)
# => 15
though of course inject
will want a method that takes an
argument. (Why this is so, is left as an argument for the reader.)
Sometimes you can omit the
&
. I'm not sure
exactly what the rule is, or even if there is one. At the cost of one
more character, you may as well just always use it.
Back to our original code, though, there's another trick we can use to simplify this.
ActiveRecord provides a method called
pluck
... and we were indeed using ActiveRecord.
pluck
sets the SQL SELECT statement to retrieve only the
columns you want. The result is an array of the values ready to be used
by your program. (If you give it more than one column to pluck, the
values are themselves arrays. However, in this case, as in the vast
majority, we were only interested in one column.) Not only does this
often make the results easier to deal with, it can also help deal with a large
dataset by saving i/o between the database and your application, memory on both
ends, etc.
So, rather than go through the hoops of retrieving the
things
associated with holder
, and then looping
through them to extract the id
column, this could be written more
simply as:
ids = holder.things.pluck(:id)
What are some of your favorite Ruby (or Rails) idioms
for making common code more concise (short but still clear)?
No comments:
Post a Comment
Note: Only a member of this blog may post a comment.