Atlas
Atlas

Reputation: 31

Return an argument's name instead of its value - Ruby

I need to print the result of a .max function, but displaying the name of its argument, not its target value (a number) used by the .max method.

def who_is_bigger(a,b,c)
  "#{[a,b,c].max} is bigger"
end

Expected result :

expect(who_is_bigger(84, 42, 21)).to eq("a is bigger")
expect(who_is_bigger(42, 84, 21)).to eq("b is bigger")
expect(who_is_bigger(42, 21, 84)).to eq("c is bigger")

Upvotes: 3

Views: 113

Answers (2)

Aaron Christiansen
Aaron Christiansen

Reputation: 11807

As far as I'm aware, there's not an effortless or easy-to-understand way to get the name of an argument from its value. (See the bottom of the answer for one way of doing it like this.)

Instead, here's the way I'd do it, which I think is nice and readable:

def who_is_bigger(a,b,c)
  values = {a: a, b: b, c: c}
  largest_key, _ = values.max_by { |key, value| value }
  "#{largest_key} is bigger"
end

This works as follows:

  1. Build a hash out of the arguments, and store it in values. For example, this hash could be { a: 84, b: 42, c: 21 }.
  2. Find the largest value in that hash using the Enumerable#max_by method. This will store an array of 2 items in largest_pair, such as [:a, 84]. Notice how the key we want is the first item, so we use destructuring to extract the first item of that pair and store it in largest_key.

  3. Construct the string and return it from the method.


If you'd like to do this in such a way where you are accessing and iterating the names and values of the parameters dynamically, you could use this:

def who_is_bigger(a,b,c)
  params = method(__method__).parameters
  values = params.map { |_, name| [name, binding.local_variable_get(name)] }.to_h
  largest_key, _ = values.max_by { |key, value| value }
  "#{largest_key} is bigger"
end

In my opinion though, this feels a bit hacky and harder-to-read than the other solution.

Upvotes: 3

Jagdeep Singh
Jagdeep Singh

Reputation: 4920

Here is another way to do this using Binding and max_by from Ruby:

def who_is_bigger(a, b, c)
  biggest = binding.local_variables.max_by do |v|
              binding.local_variable_get(v) || -Float::INFINITY
            end
  "#{biggest} is biggest"
end

who_is_bigger(10, 21, 30)
=> "c is biggest"

who_is_bigger(40, 31, 30)
=> "a is biggest"

Upvotes: 2

Related Questions