Ruby - Create a hash, where Keys are newly initialized Array objects - ruby

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?

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

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.

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.

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.

Related

Saving pixel color in array Ruby

I use CarrierWave and MiniMagick for work with images. I upload image. Right now I need to get values of all pixels in image and store them into array
If I use next code it works well
def pixelcolor
#image = MiniMagick::Image.open(image.path)
return #image.get_pixels[56][34][1] # 56 and 34 - random numbers for row and column
end
next code works as well
def pixelcolor
#image = MiniMagick::Image.open(image.path)
width = #image.width
color = Array.new
for i in 0..width
color[i] = i
end
return color
end
But if I try to do next code it doesn't work
def pixelcolor
#image = MiniMagick::Image.open(image.path)
width = #image.width
color = Array.new
for i in 0..width
color[i] = #image.get_pixels[45][65][0]
end
return color
end
As result I need something like this
def pixelcolor
#image = MiniMagick::Image.open(image.path)
width = #image.width
height = #image.height
red = Array.new
green = Array.new
blue = Array.new
for i in 0..width
for j in 0..height
red[i][j] = #image.get_pixels[width][height][0]
green[i][j] = #image.get_pixels[width][height][1]
blue[i][j] = #image.get_pixels[width][height][2]
end
end
return red, green, blue
end
Much time ago I have created very similar project in C++ for working with images so I have idea of what do to but not sure how do do it correct in Ruby as I'm new in it
The problem you're facing stems from the fact that Ruby doesn't have a concept of "multidimensional arrays", individual arrays are always one-dimensional.
You can, however, have nested arrays, which are arrays that contain arrays, like [[], [], []].
Let's have a look at two particular places:
red = Array.new # Note: this could as well be `red = []`
So you have assigned red a single empty array. Alright. Here's what you do later on:
red[i][j] = ...
We don't even need the rest of the line, it's visibly broken already.
On the very first iteration red is still an empty array. Thus, red[i] is nil, since there's absolutely nothing inside. nil does not have array accessors ([] and []=), so you'll be getting errors trying to read or write to it as if it were an array.
So you need red[i] to be an actual array, so you can put data into it. Let's make it happen.
Since you know the size of your arrays in advance, you can just allocate and populate red with with arrays of appropriate length immediately:
red = Array.new(width) { Array.new(height) }
For instance, for width = 2 and height = 3 here's the result you'd get:
[
[nil, nil, nil],
[nil, nil, nil]
]
So now that you have allocated all the cells that you'll need, you can start overwriting the nils with something meaningful.
See the docs for Array.new for the various ways to construct an array.
Careful:
I have used a "blocky" form of Array.new for a reason.
Beginners often make a mistake of using a single value to populate the array like so:
red = Array.new(width, Array.new(height))
While this form has its uses, it won't have the effect that you need: all the entries in red will refer to one and the same array, so modifying any single row would look as if all of them are modified.
Side note: you probably want to swap width and height in dimensions, since in graphics programs usually deal with arrays of rows, not columns. Depends, though. Might not have any effect on your particular problem.

Not displaying it's corresponding values with it's key for Hash

Ok i am not here to ask for an answer. But to be honest i am not really good in class variable. So i would appreciate you can guide me along with this piece of code.
I have read on class variable at those docs. I some what kind of understand it. But it comes to applying it for my own use. I would get confused.
class Square
##sqArray = {}
#attr_accessor :length
def initialize
if defined?(##length)
randno = "%s" % [rand(20)]
##length = randno.to_i
##sqArray = ##length
else
randno = "%s" % [rand(20)]
##length = randno.to_i
##sqArray = ##length
end
end
def Area
##area = ##length * ##length
return ##area
##sqArray[##length.to_sym] = ##area
puts ##sqArray
end
end
s1 = Square.new
puts s1.Area
Let me explain this piece of code. Basically every time i create a Square object it would go to initialize method. A random number will be generated and pass it to ##length, and ##length will be assigned to hash ##sqArray as it's key. But now the problem is when i create a new object s1. When i want to display the Area i want to test out to print the hash ##sqArray with it's length as it's key and area as it's value. But now the problem is only returning it's area only. e.g 114 only.
suppose to be e.g [ 24 => 114]
When defining the object's property (i.e. it's length), the correct approach is to use an instance variable, not a class variable. This is because (in your particular example), length is an attribute of a specific square and not something that applies to all squares. Your code should look something like this:
class Square
def initialize(length = rand(20))
#length = length
end
def area
#length * #length
end
end
s1 = Square.new
puts s1.area
Now, I am a little unclear what exactly you aim to achieve by use of that class variable ##sqArray - but for example, you could use this store a list of all defined Squares:
class Square
##squares_list = []
def self.all_known
##squares_list
end
def initialize(length = rand(20))
#length = length
##squares_list << self
end
def area
#length * #length
end
end
This would allow you to write code like:
s1 = Square.new #=> #<Square:0x0000000132dbc8 #length=9>
s2 = Square.new(20) #=> #<Square:0x000000012a1038 #length=20>
s1.area #=> 81
s2.area #=> 400
Square.all_known #=> [#<Square:0x0000000132dbc8 #length=9>, #<Square:0x000000012a1038 #length=20>]
Class variables have some odd behaviour and limited use cases however; I would generally advise that you avoid them when starting out learning Ruby. Have a read through a ruby style guide to see some common conventions regarding best practice - including variable/method naming (use snake_case not camelCase or PascalCase), whitespace, etc.

Using Strings as Variable/Object Names in Ruby

I am dealing with fractals. You start with a rectangle, and that shape is decreased by a given decay rate. I have it set up to do the first 10 iterations of the given scenario, and each scenario looks like this:
y_1 = dec_y(y_1)
y_2 = dec_y(y_2)
a_y = [y_1, y_2]
rect_1 = TkcRectangle.new(canvas, [0,0], a_y)
where dec_y is defined as the following:
def dec_y(y)
to_ret = y / $rate
return to_ret
end
I want to turn the first snippet into a function/method (not exactly sure what the Ruby term is...), so that each iteration will just be a single line referencing a method, which makes the problem more extensible. But, I need each TkcRectangle to have a different name. The way I want to set it up, each TkcRectangle will have the same name. But, if I can set the name of the object to a string passed as an argument, then I should not have a problem.
How do I define the name of an object with a given string?
Edit : Code has not been tested, but will give you the idea.
Instead of naming each element, you can use an array and use the index instead
rectangles_array = Array.new
for each loop
rectangles_array << create_rectangle_object(y_1, y_2, canvas)
end for each loop
def dec_y(y)
to_ret = y / $rate
return to_ret
end
def create_rectangle_object(y_1, y_2, canvas)
return TkcRectangle.new(canvas, [0,0], [dec_y(y_1), dec_y(y_2)])
end
If you really want to name it read about structs.. Something like
MyRectangleStruct = Struct.new(:obj_name, :x1, :y1, :x2, :y2)
puts MyRectangleStruct.new(:obj_name => 'First_rec', .....)
define_method(method_name, &block)
with method_name being any string and &block being a block of ruby code; usually it looks something like this:
define_method(method_name) do
your code goes here
end

Subtraction of two arrays with incremental indexes of the other array to a maximum limit

I have lots of math to do on lots of data but it's all based on a few base templates. So instead of say, when doing math between 2 arrays I do this:
results = [a[0]-b[1],a[1]-b[2],a[2]-b[3]]
I want to instead just put the base template: a[0]-b[1] and make it automatically fill say 50 places in the results array. So I don't always have to manually type it.
What would be the ways to do that? And would a good way be to create 1 method that does this automatically. And I just tell it the math and it fills out an array?
I have no clue, I'm really new to programming.
a = [2,3,4]
b = [1,2,3,4]
results = a.zip(b.drop(1)).take(50).map { |v,w| v - w }
Custom
a = [2,3,4..............,1000]
b = [1,2,3,4,.............900]
class Array
def self.calculate_difference(arr1,arr2,limit)
begin
result ||= Array.new
limit.send(:times) {|index| result << arr1[index]-arr2[index+=1]}
result
rescue
raise "Index/Limit Error"
end
end
end
Call by:
Array.calculate_difference(a,b,50)

optimize this ruby code, switch arrays to sets/hash?

I need to optimize this code. Any suggestions to make it go faster, please tell me. I don't have a specific amount that I want it to go faster, any suggestion would be helpful. In terms of complexity I want to keep it below O(n^2)
I'm wondering if trying to convert the array that I'm using into like a set or hash because that is quicker right? How much faster in terms of complexity might this allow me to run?
The main problem I think might be my use of the ruby combination function which runs pretty slow, does anyone know exactly the complexity for this ruby function? is there a faster alternative to this?
the point of this code is basically to find the single point that is the shortest combined distance from all the other points ie (the friends house that is most convenient for everyone to go to). there is a little extra code here which has some debugging/printing functions.
class Point
attr_accessor :x, :y, :distance, :done, :count
def initialize(x,y)
#x = x
#y = y
#distance = 0
#closestPoint = []
#done = false
#count = 0
end
end
class Edge
attr_accessor :edge1, :edge2, :weight
def initialize(edge1,edge2,weight)
#edge1 = edge1
#edge2 = edge2
#weight = weight
end
end
class AdjacencyList
attr_accessor :name, :minSumList, :current
def initialize(name)
#name = name
#minSumList = []
#current = nil
#vList = []
#edgeList = []
end
def addVertex(vertex)
#vList.push(vertex)
end
def generateEdges2
minSumNode = nil
current = nil
last = nil
#vList.combination(2) { |vertex1, vertex2|
distance = distance2points(vertex1,vertex2)
edge = Edge.new(vertex1,vertex2,distance)
if (current == nil)
current = vertex1
minSumNode = vertex1
end
vertex1.distance += distance
vertex2.distance += distance
vertex1.count += 1
vertex2.count += 1
if (vertex1.count == #vList.length-1)
vertex1.done = true
elsif (vertex2.count == #vList.length-1)
vertex2.done = true
end
if ((vertex1.distance < minSumNode.distance) && (vertex1.done == true))
minSumNode = vertex1
end
##edgeList.push(edge)
}
return minSumNode.distance
end
def generateEdges
#vList.combination(2) { |vertex1, vertex2|
distance = distance2points(vertex1,vertex2)
#edgeList.push(Edge.new(vertex1,vertex2,distance))
}
end
def printEdges
#edgeList.each {|edge| puts "(#{edge.edge1.x},#{edge.edge1.y}) <=> (#{edge.edge2.x},#{edge.edge2.y}) weight: #{edge.weight}"}
end
def printDistances
#vList.each {|v| puts "(#{v.x},#{v.y} distance = #{v.distance})"}
end
end
def distance2points(point1,point2)
xdistance = (point1.x - point2.x).abs
ydistance = (point1.y - point2.y).abs
total_raw = xdistance + ydistance
return totaldistance = total_raw - [xdistance,ydistance].min
end
#pointtest1 = Point.new(0,1)
#pointtest2 = Point.new(2,5)
#pointtest3 = Point.new(3,1)
#pointtest4 = Point.new(4,0)
graph = AdjacencyList.new("graph1")
gets
while (line = gets)
graph.addVertex(Point.new(line.split[0].to_i,line.split[1].to_i))
end
#graph.addVertex(pointtest1)
#graph.addVertex(pointtest2)
#graph.addVertex(pointtest3)
#graph.addVertex(pointtest4)
puts graph.generateEdges2
#graph.printEdges
#graph.printDistances
Try to do this, and then post some more code:
ruby -rprofile your_script your_args
This will run the script under the profiler, and generate a nice table with results. If you post that here, it's more likely to get better help. Plus, you will have a more exact idea of what's consuming your CPU cycles.
Sets are basically hashes, and the advantage of hashes over arrays is O(1) find operations. Since you are simply iterating over the entire array, hashes will not offer any speed improvements if you simply replace the arrays with hashes.
Your real problem is that the running time of your algorithm is O(n^2), as in given a set of n points it will have to perform n^2 operations since you're matching every point with every other possible point.
This can be somewhat improved using hashes to cache values. For example, lets say you want the distance between point "a" and point "b". You could have a hash #distances which stores #distances["a,b"] = 52 (of course you'll have to be smart about what to use as the key). Basically just try to remove redundant operations wherever you can.
That said, the largest speed boost would be from a smarter algorithm, but I can't think of something applicable off the top of my head right now.
There's something many people know, and it won't cost you anything.
While you're trying to guess how to make the code faster, or scouring the internet for some kind of profiler, just run the program under the debugger and interrupt it while it's being slow.
Do it several times, and each time take careful note of what it's doing and why.
Here's an example in python.
The slower it is, the more obvious the problem will be.

Resources