Search code examples
rubyruby-2.4

Is there a way to take an array and modify each individual element in a unique way using a single line of code?


I'm attempting to modify a variable string in Ruby 2.4 that contains both a number and a unit of measurement. My goal is to take the following string, round the numeric portion, and capitalize the unit portion:

my_string = "123.456789 dollars"

There are all sorts of ways that I know of to complete this operation with multiple lines of code, but I'm working with a unique interface and in a unique environment where the goal is to write this using a single line of code that is as short as possible. I know that I can do something like this:

my_array =my_string.split; my_array[0].to_f.round(2).to_s + " " + my_array[1].capitalize

...but my goal is to do away with the semicolons, use less characters, and to avoid having to call the array twice.

My idea was to do something along the lines of:

my_string.split.*do_this*(self.to_f.round(2), self.capitalize).join(" ")

Is there any way to do something like this PRIOR to version 2.5?

I'm open to other ideas as well, but again, the goal is a single line of code that is as short as possible, and the ability to modify the 2 elements using different parameters on each.

Please note: Upgrading to a newer version of Ruby is not an option. 2.4 is the version currently hard coded into the software package we’re working with.


Solution

  • I found that in Ruby 2.4 we can use the instance_eval method in at least a few different ways such as:

    my_string.split.instance_eval { |f, s| "#{f.to_f.round(2)} #{s.capitalize}" }
    

    or

    my_string.split.instance_eval { |a| "#{a[0].to_f.round(2)} #{a[1].capitalize}" }
    

    or

    my_string.split.instance_eval { |f, s| [f.to_f.round(2), s.capitalize]}.join(" ")
    

    We can also use the tap method which requires just a few more characters:

    my_string.split.tap { |a| a[0..-1] = [a[0].to_f.round(2), a[1].capitalize]}.join(" ")
    

    Or we can even use the map method by creating a nested array like in these 2 examples:

    [my_string.split].map{|a| "#{a[0].to_f.round(2)} #{a[1].capitalize}"}.join
    

    and

    [my_string.split].map{|a| [a[0].to_f.round(2)," ",a[1].capitalize]}.join
    

    And just as an added bonus, we can even do something like the following which doesn't require using a block:

    (a, b = my_string.split).replace([a.to_f.round(2)," ",b.capitalize ]).join