Mushy
Mushy

Reputation: 172

Get variable whose value is the maximum in an array

If I have an array of variables in Ruby:

a = 4
b = 7
c = 1

array = [a, b, c]

How can I get access to the name of the variable that has the highest value? (In this example b) I want to retrieve a reference to the element with the highest value, to be able to later manipulate it:

b += 10 

I tried array.max but that just returns the max value 7

Upvotes: 4

Views: 323

Answers (4)

ptierno
ptierno

Reputation: 10074

You can use Array#index for this..

1.9.3-p484 :008 > max_elem = array.index(array.max)
 => 1
1.9.3-p484 :009 > array[max_elem] = array[max_elem] + 10
 => 11
1.9.3-p484 :010 > array
 => [4, 17, 1]
1.9.3-p484 :011 >

Upvotes: -1

David Grayson
David Grayson

Reputation: 87386

When you build an array by writing array = [a, b, c], the spots in the array do not retain any kind of association with the variables named a, b, and c, so you there is no way to do exactly what you are talking about. Those variables can change without affecting the array.

You could change the way you are storing your data in order to make this possible, but without knowing what data you are storing it is hard to recommend a good way to do it. You could consider storing your data inside a hash instead of in loose variables and an array:

hash = { a: 4, b: 7, c: 1}

Upvotes: 6

Wand Maker
Wand Maker

Reputation: 18762

If you are trying to manipulate the maximum of three values, then, you are better off doing something like this:

array.max + 10  # or any other manipulation for that matter.

If you are asking what you are asking for academic purposes, then, you could use Ruby's Binding to inspect values of local variables.

a = 4
b = 7
c = 1

array = [a, b, c]

# Select variable whose value matches with maximum of three elements
# Variable's name as symbol is obtained by this process
var = binding.send(:local_variables).select do |i| 
    array.max.eql? binding.local_variable_get(i)
end.first
#=> :b

# Update value of variable using symbol representing  its name
binding.local_variable_set(var, binding.local_variable_get(var) + 10)

puts b
#=> 17

You could also use Binding#eval to manipulate the variable, for e.g.:

binding.eval("#{var.to_s} += 10")
#=> 17

Note that we need to use var.to_s as var contains a symbol, and we need to convert it to string to get variable name string that can be used in eval.

Upvotes: 1

Drenmi
Drenmi

Reputation: 8777

Ruby does not support passing objects by reference, which is essentially what you are trying to do. When you call array.max, you are passed a copy of the Fixnum 7, and assignment and modification will be applied to this copy.

That is, you can not store a reference to this Array element, and modify it later. You can modify the maximum value on the spot, however, using:

array[array.index(array.max)] = array.max + 10
#=> 17

array
#=> [4, 17, 1]

Note that when there are duplicate values in the Array, array.index(array.max) will return the index of the first one.

Storing the index for later use is not a solid solution, since, while Arrays in Ruby are ordered, the Array or its elements can be modified between the point you retrieve the index, and the point where you decide to change the corresponding value.


Another answer suggests there's a hack to mimic pass-by-reference behaviour, but in doing this, you're working against the intention of the language, and it could lead to a lot of pain down the line.

Upvotes: 4

Related Questions