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?

Was it helpful?

Solution

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

OTHER TIPS

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'.

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)}

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.

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) }
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top