By now many Ruby programmers are familiar with the magic that is Symbol#to_proc.
If you haven’t seen anything such as youngest = people.map(&:age).min yet here’s a quick explanation of the syntax.
- Ruby calls
to_procon the block parameter if it is not already aProc. (coercion) - Since a symbol is a message,
Symbol#to_procsimply constructs a block that sendsself(the symbol) to the first block parameter. - The code above is equivalent to
youngest = people.map { |p| p.age }.min - The few characters saved are significant if you are sufficiently lazy, or if you frequently chain short blocks like that together.
This is a great way to clean up your code, and the brevity will have you groaning when you have to type out something such as: birth_years = people.map { |p| p.birthdate.year }
But there is a simple way to make that shorter using a trick similar to Symbol#to_proc. To me it seems logical that if map(&:sym) sends :sym to each element of the enumerable object, then an array of symbols should chain the calls together on each element of the enumerable.
If you’ve looked at the implementation of Symbol#to_proc in Rails then this should look familiar as it’s nearly the same code.
1 2 3 4 5 6 7 8 9 10 11 |
module ArrayExtensions # Turns an array of symbols into a simple proc, which is especially useful for enumerations. Examples: # # # The same as people.map { |p| p.birthdate.year } # people.map(&[:birthdate, :year]) def to_proc Proc.new do |*args| self.inject(args.shift) { |obj, msg| obj = obj.__send__(msg, *args) } end end end |
This makes the following code valid, and equivalent to the previous example using birth_years.
birth_years = people.map(&[:birthdate, :year]) |
You can use strings, like so:
birth_years = people.map(&%w[birthdate year]), but I don’t dig the &% part of it. To each their own.
Then all that needs to be done is load the extension. I’m doing all this in the context of Rails so I’m setting this up in my environment.rb.
Array.send(:include, ArrayExtensions) |
