Tutorial :Ruby: How do I use symbols to represent things in an array?



Question:

I have an array of arrays that looks like this:

fruits_and_calories = [    ["apple", 100],    ["banana", 200],    ["kumquat", 225],    ["orange", 90]  ]  

I also have a method I want to invoke on each element of the array:

fruits_and_calories.each do |f| eat(f[0], f[1])  

I'd really like to be able to say something like:

fruits_and_calories.each do |f| eat(f[:name], f[:calories])  

Is there a way that I can pull this off without having to change each item in the array (for example, by iterating through it and somehow adding the symbols in)? Or, if that's too hard, is there a better alternative?


Solution:1

The best answer is not to use an Array at all, but to use a Hash:

fruits_and_calories = { :apple => 100,                          :banana => 200,                          :kumquat => 225,                          :orange => 90}  fruits_and_calories.each do |name, calories|     eat(name, calories)  end  


Solution:2

Without changing the data structure at all, you could change the block arguments to achieve the same thing:

fruits_and_calories.each  do |name, calories|  eat(name, calories);  end  

This works because Ruby will auto-expand the inner arrays (["apple", 100], etc) into the argument list for the block ('do |name, calories| ... end'). This is a trick that Ruby inherited from Lisp, known as 'destructuring arguments'.


Solution:3

Pesto's answer (use a hash) is a good one, but I think I'd prefer to use a Struct.

Fruit = Struct.new(:name, :calories)    fruits = [    Fruit.new("apple", 100),    Fruit.new("banana", 200),    Fruit.new("kumquat", 225),    Fruit.new("orange", 90)  ]    fruits.each {|f| eat(f.name, f.calories)}  

This also lends itself to changing eat from taking both the name and calories, to taking a fruit instance:

fruits.each {|f| eat(f)}  


Solution:4

Is there some reason it must be an array, per se? That seems to cry out to be a hash, or a class for Fruit.


Solution:5

An array is always indexed by numbers, so as far as I know using the standard array it's not possible.

Personally I'd just opt for using a comment above the code to hint what f[0] and f[1] stands for.

But if you are hell bent on doing it I guess some duck-typing on the Array class works:

  class Array    def name ; self[0] ; end    def calories ; self[1] ; end  end    # then call it with:  fruits_and_calories.each {|f| eat(f.name, f.calories) }  

Note:If u also have question or solution just comment us below or mail us on toontricks1994@gmail.com
Previous
Next Post »