There are many blog posts that basically say, "Given problem X, you can solve it elegantly in language Y. Language Y is awesome!" The problem with this is that it is essentially a strawman argument. Who is to say that problem X would even be the same in other languages, if you take their conventions and idioms into account?
An example is a post "Python is still awesome" where problem X is "Given a list of dictionaries, sum the values of one of the dictionary keys."
d = [{'value':1, 'title':'one'}, {'value':2}, {'value':3}, {'value':4, 'title':'four'}, ]
The solution is:
s = sum([i['value'] for i in d])
Now, before I continue, let me say that the solution is very elegant. I like Python, and list comprehensions are indeed awesome. They are one of my favorite things about Python. (Nested ones get a little hard to grok, but I digress...)
So I thought about how I would solve that particular Problem X in RoR:
d = [{:value => 1, :title => "one"}, {:value => 2}, {:value => 3}, {:value => 4, :title => "four"}] s = d.collect{|item| item[:value]}.sum
Pretty good, but only because the sum method is added to Enumerable in ActiveSupport. Outside of Rails you'd have to use inject, and it would not be as nice. (Hopefully others think so too, and sum makes it into Ruby 2.0.) Overall I like the list-comprehension solution a bit better.
However—and this is the crux of the issue—in Ruby, Problem X generally does not occur. Why? Ruby makes it so easy to make objects for the items that I rarely need data structures like that. I can create a simple Item class:
class Item attr_accessor :title, :value end
Now I have Item objects, complete with accessors (item.title, item.value, etc). This is much more elegant than data structures, particularly if there will be a lot of manipulation of the data. So instead of Problem X, the problem boils down to summing the values of a list of Item objects instead of anonymous hashes. And that would look like this:
s = d.collect(&:value).sum
In broad strokes, it's very easy to see what's happening there: you're collecting values from d and summing them. This uses the nifty Symbol#to_proc feature in Ruby 1.9+ that allows you to convert the symbol :value to a Proc object, which essentially makes it shorthand for:
s = d.collect{|x| x.value}.sum
It's got a bit of a magicky feel, especially for those who are not well-versed in Ruby. Fortunately, Sam Huri provides a clever solution, Enumerable#pluck, which adds the pluck method to all things enumerable. The pluck method is basically a short one-liner. With it, the solution to the problem becomes:
s = d.pluck(:value).sum
One character different, but much more understandable. I hope pluck makes it into Ruby 2.0, too.
After this long example, what I have is essentially a solution to a different problem. But it is necessarily so, because languages tend to be used differently. In Perl or PHP, I would use an array-of-hashes because creating objects with accessors is not as simple, nor is it convention.
2 comments ↓
Good stuff!
Note that
s = d.collect(&:value).sum
is valid in Ruby 1.8.6.
///ark
Mark,
That only works inside Rails. Otherwise you need Ruby 1.9.
Leave a Comment