bwizzy
bwizzy

Reputation: 1599

can you define a block inline with ruby?

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

Answers (4)

tadman
tadman

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

Chuck
Chuck

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

krdluzni
krdluzni

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

Ken
Ken

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

Related Questions