Alec
Alec

Reputation: 2762

What's the cleanest way of chaining stream functions in Ruby?

Let's say I have an array of stuff that I want to perform all sorts of wacky operations on. For example:

my_array = [
  { name: 'Lyra', age: 12 },
  { name: 'Harry', age: 11 },
  { name: 'Kestrel', age: 13},
]

I want to filter out anyone under the age of 12, change all names to symbols, and then sort them by age (say).

This can be achieved with:

new_array = my_array.
  select { |person| person[:age] > 11 }.
  map { |person| person.merge(name: person[:name].to_sym) }.
  sort_by { |person| person[:age] }

So that's all dandy. But what if I have arbitrarily complex logic I need to do my selecting/mapping/sorting/etc.?

Standard practice says that multi-line blocks with braces are to be avoided (and some linters even outright forbid it). Yet, the alternative is to start chaining do..end blocks, which looks even ickier:

new_array = my_array.
  select do |person|
    # Do something complex
  end.
  map do |person|
    # More complex stuff
  end.
  sort_by do |person|
    # Yet more complex stuff
  end

Does the Ruby community have any advice on best practice for chaining this sort of thing? For example, is it nicer to define a Proc (or similar), and pass that into the block?

Upvotes: 4

Views: 165

Answers (1)

Stefan
Stefan

Reputation: 114188

You could create a copy and use destructive methods instead:

new_array = my_array.dup

new_array.select! do |person|
  # Do something complex
end

new_array.map! do |person|
  # More complex stuff
end

new_array.sort_by! do |person|
  # Yet more complex stuff
end

Upvotes: 4

Related Questions