0

Please bear with me...I need basic concepts...I am not aware of advanced prog concepts yet.

I have a class called Circle which initializes and calculates area

class Circle
   def initialize (radius)
     @radius = radius
   end


   def area
     3.14*@radius*@radius
   end
end

I want to take user input and create however many instances of Circle objects and its sides.

p "How many Circles"
i = gets.to_i
j = 1

while j != i+1
p "Enter radius of Circle #{j}"
$s << Circle.new(gets.to_i)
j = j +1
end

The $s[] now holds array of objects I created.

Now, I want to do something like,

area_array[] = 0
area_array[Circle1] = Circle1.area
area_array[Circle1] = Circle2.area  

and so on...where Circle1 and Circle2 are the objects I created earlier in my while loop....

Can someone tell me how can I put each of the created object in another array and assign an area value to it?

Yubaraj
  • 3,800
  • 7
  • 39
  • 57

4 Answers4

2

Do you need another array because you will modify or destroy the properties of the Circles in the first array? If so, and you can rely on the Cirlces' order in the array remaining the same, then just use the index value to correlate the values:

circle_area_hash = $s.reduce{|a, c| a[c.object_id] = c.area }

Also, consider that for your analyses, you may care more about the values, than the objects, per se. So then you could create

circle_area_hash = $s.reduce do |a, c| 
  a[c.area] = (a[c.area].nil?) ? [c] : a[c.area] << c 
end

This make the hash-keys bey the area value as, and the hash-values are each an array of the objects that have that area.

Then to get the key (largest area) you can:

circle_area_hash.max_by{|k,v| v.count}

Also, as a thought:

puts "How many Circles"
$s = (1...gets.to_i).each |j|
  puts "Enter radius of Circle #{j}"
  $s << Circle.new(gets.to_i)
end

$s[3].area
Community
  • 1
  • 1
New Alexandria
  • 6,951
  • 4
  • 57
  • 77
  • Hi, I wanted to create a hash which will eventually store areas of objects of multiple classes...such as Circle, Rectangle, Square, etc...So I need the Key + Value so that I can look up whose Area is the highest in the given list...I know it can be done differently but this is what I want to do now I guess. Sorry...I have definately tried other ways, just wanted to try this one.. – user2800586 Sep 20 '13 at 20:47
  • @user2800586 I've updated again in attempts to show you a way to solve the *bigger problem* that I think you're trying to solve – New Alexandria Sep 20 '13 at 21:01
  • @MarianTheisen yes, that was a pretty wicked thing to discover. I wonder how that works – New Alexandria Sep 20 '13 at 21:47
2

To create a new array of areas:

area_array = $s.map{ |circle| circle.area }
area_array = $s.map( &:area )               # Same thing, but shorter

To create one big hash:

areas = Hash[ $s.map{ |circle| [ circle, circle.area ] } ]

This creates an array of arrays like:

[
  [ <Circle @radius=3>, 28.27 ],
  [ <Circle @radius=4>, 50.27 ],
  …
]

…and then uses the Hash.[] method to convert that into a Hash.

Another technique is:

areas = $s.inject({}){ |hash,circle| hash.merge(circle=>circle.area) }

For more details, read up on Array#map and Enumerable#inject.

However, why would you want to create this hash? It seems like you're perhaps wanting to only calculate the area once each. Although it's not needed for this simple calculation, you can memoize a method's return value with a pattern like this:

class Circle
  def initialize(radius)
    @radius = radius
  end
  def area
    @area ||= Math::PI*@radius*@radius
  end
end

This will calculate the area the first time it's needed, and store it in an instance variable; thereafter it will just use the value of that variable as the return value of the method, without needing to recalculate it.

Phrogz
  • 296,393
  • 112
  • 651
  • 745
1

This is very straightforward. You should just iterate over $s, using each element as a hash key and the result of its area as the corresponding value.

Another few points that should be useful to you:

  • You can use Math::PI instead of 3.14

  • You should only use p for debugging. It prints the result of the inspect method of its parameter, which is rarely what you want for tidy output. Use print if you want to make your newlines explicit in the string, or puts to append a newline if there isn't one already

  • It is rarely appropriate to use while in Ruby. In this instance you just want i.times do { ... }

 

class Circle

  def initialize (radius)
    @radius = radius
  end

  def area
    Math::PI * @radius * @radius
  end

end

print 'How many Circles: '
i = gets.to_i

shapes = []
i.times do |n|
  print "Enter radius of Circle #{n+1}? "
  shapes << Circle.new(gets.to_i)
end

area_hash = {}
shapes.each do |shape|
  area_hash[shape] = shape.area
end

However it seems more appropriate to memoize the area method here, writing it as

def area
  @area = Math::PI * @radius * @radius unless @area
  @area
end

Then you can use the method repeatedly and the calculation will be done only once.

Borodin
  • 126,100
  • 9
  • 70
  • 144
0

After reading your comment on NewAlexandria's answer, perhaps something like this would work for you:

p "How many Circles"
(1..gets.to_i) do |j|
  c = Circle.new
  p "Enter radius of Circle #{j}"
  s[c] = c.area(gets.to_i)}
end

where s is a pre-defined hash that may contain keys for instances of other circles, rectangles, etc.

This only makes sense, however, if you plan to add additional constants or methods to your shape classes that you will want to reference with the keys of s.

You should edit your question to incorporate your comment above.

Cary Swoveland
  • 106,649
  • 6
  • 63
  • 100