Reputation: 1599
Is it possible to define a block in an inline statement with ruby? Something like this:
tasks.collect(&:title).to_block{|arr| "#{arr.slice(0, arr.length - 1).join(", ")} and #{arr.last}" }
Instead of this:
titles = tasks.collect(&:title)
"#{titles.slice(0, titles.length - 1).join(", ")} and #{titles.last}"
If you said tasks.collect(&:title).slice(0, this.length-1)
how can you make 'this' refer to the full array that was passed to slice()?
Basically I'm just looking for a way to pass the object returned from one statement into another one, not necessarily iterating over it.
Upvotes: 3
Views: 3326
Reputation: 211590
Although there are many good answers here, perhaps you're looking for something more like this in terms of an objective:
class Array
def andjoin(separator = ', ', word = ' and ')
case (length)
when 0
''
when 1
last.to_s
when 2
join(word)
else
slice(0, length - 1).join(separator) + word + last.to_s
end
end
end
puts %w[ think feel enjoy ].andjoin # => "think, feel and enjoy"
puts %w[ mitchell webb ].andjoin # => "mitchell and webb"
puts %w[ yes ].andjoin # => "yes"
puts %w[ happy fun monkeypatch ].andjoin(', ', ', and ') # => "happy, fun, and monkeypatch"
Upvotes: 0
Reputation: 237060
You're kind of confusing passing a return value to a method/function and calling a method on the returned value. The way to do what you described is this:
lambda {|arr| "#{arr.slice(0, arr.length - 1).join(", ")} and #{arr.last}"}.call(tasks.collect(&:title))
If you want to do it the way you were attempting, the closest match is instance_eval
, which lets you run a block within the context of an object. So that would be:
tasks.collect(&:title).instance_eval {"#{slice(0, length - 1).join(", ")} and #{last}"}
However, I would not do either of those, as it's longer and less readable than the alternative.
Upvotes: 4
Reputation: 797
I don't really understand why you would want to, but you could add a function to the ruby classes that takes a block, and passes itself as a parameter...
class Object
def to_block
yield self
end
end
At this point you would be able to call:
tasks.collect(&:title).to_block{|it| it.slice(0, it.length-1)}
Of course, modifying the Object class should not be taken lightly as there can be serious consequences when combining with other libraries.
Upvotes: 1
Reputation:
I'm not sure exactly what you're trying to do, but:
If you said tasks.collect(&:title).slice(0, this.length-1) how can you make 'this' refer to the full array that was passed to slice()?
Use a negative number:
tasks.collect(&:title)[0..-2]
Also, in:
"#{titles.slice(0, titles.length - 1).join(", ")} and #{titles.last}"
you've got something weird going on with your quotes, I think.
Upvotes: 1