Ruby has this handy block shortcut map(&:to_i)
for map { |x| x.to_i }
. However, it is limited to argument-less method invocations.
To call a method with an argument, you usually need to use the full block form. A common and annoying case is retrieving values from a list of hashes (imagine using a JSON API):
users = [ { name: 'Dominik', color: 'blue' }, { name: 'Stefan', color: 'red'} ]
names = users.collect do |user|
user[:name]
end
If you're using Rails 5+, this example is covered by Enumerable#pluck
(users.pluck(:name)
). But with a little extension, you can have this for any method:
Block shortcut with arguments
By defining Symbol#with
, you can start passing arguments to the short block form. (Ruby 2.7+ syntax)
class Symbol
def with(...)
->(caller, *rest) {
caller.send(self, *rest, ...)
}
end
end
Using
Array#dig
Show archive.org snapshot
and
Hash#dig
Show archive.org snapshot
, we can now do this:
users = [ { name: 'Dominik', color: 'blue' }, { name: 'Stefan', color: 'red'} ]
names = users.collect &:dig.with(:name)
Note that this example is very well suited for working with JSON, where you're usually collecting values at a certain path through the JSON data structure. This is what #dig
was designed for. Example: projects.collect &:dig.with(:company, :tags, 0, :name)
will call project[:company][:tags][0][:name]
for each project and collect the results.
Credits to the linked StackOverflow question.