Iterate over array and first again - ruby

In order to export the coordinates of a polygon with n points I need to have n+1 points. The additional one is supposed to "close" the polygon. Currently I simply iterate over the whole array and do it manually again for the first:
face.outer_loop.vertices.each do |g|
xml.Grafic(
:X=>g.position.x,
:Y=>g.position.y,
:Z=>g.position.z)
end
xml.Grafic(
:X=>face.outer_loop.vertices[0].x,
:Y=>face.outer_loop.vertices[0].y,
:Z=>face.outer_loop.vertices[0].z)
Is there a more elegant solution?

I can think about:
COORDS = %i(X Y Z)
(face.outer_loop.vertices + [face.outer_loop.vertices.first]).each do |g|
values = COORDS.map do |c|
g.position.public_send "#{c.to_s.downcase}"
end
xml.Graphic COORDS.zip(values).to_h
end
or use each_with_object to not pollute coords in the global namespace:
(face.outer_loop.vertices + [face.outer_loop.vertices.first]).each_with_object(%i(X Y Z)) do |g, coords|
values = coords.map do |c|
g.position.public_send "#{c.to_s.downcase}"
end
xml.Graphic coords.zip(values).to_h
end

Related

Please explain Ruby's square root (sqrt) value comparison

I have a Ruby class that compares the size of two values of x and y by inheriting the built-in module Comparable and sqrt methods. But unfortunately, I don't understand what def scalar in the code is calculating?
In the example below, Ruby's execution results in that v1 is greater than v2, but if I print the results of v1 and v2 alone, I get nothing but nonsense. So my second question is, what are the resulting values for v1 and v2?
class Vector
include Comparable
attr_accessor :x, :y
def initialize(x, y)
#x, #y = x, y
end
def scalar
Math.sqrt(x ** 2 + y ** 2)
end
def <=> (other)
scalar <=> other.scalar
end
end
v1 = Vector.new(2, 6)
v2 = Vector.new(4, -4)
puts v1 #=> #<Vector:0x000055a6d11794e0>
puts v2 #=> #<Vector:0x000055a6d1179490>
p v1 <=> v2 #=> 1
p v1 < v2 #=> false
p v1 > v2 #=> ture
Math.sqrt(x ** 2 + y ** 2) is using good old Pythagoras to calculate the Cartesian distance of the vector's endpoint from the origin, i.e., the length of the vector. For v1 this is 6.324555320336759, and for v2 the result is 5.656854249492381.
To inspect a Ruby object use p rather than puts.
p v2 # <Vector:0x0000000109a1eb40 #x=4, #y=-4>
what are the resulting values for v1 and v2?
Override to_s
class Vector
def to_s
"x = #{x} : y = #{y} : scalar = #{'%.6f' % scalar}"
end
end #Vector
And you get this output:
puts v1 # => x = 2 : y = 6 : scalar = 6.324555
puts v2 # => x = 4 : y = -4 : scalar = 5.656854
scalar would be better named magnitude because it is calculating the length of a vector using the Pythagorean Theorem. If you are unfamiliar with vectors and Pythagorean Theorem, I suggest you study the mathematical concepts here before you continue with much more code. Those concepts will be critical in understanding how they are used in code.

how does this checks surrounding cells in a 2D array

I am attempting to write a battleship game in ruby. I came across a code snippet that I think I grasp, but was hoping you all could maybe offer some clarification. The [-1,0,1] is what is throwing me. This is to check a 2D array. Thank you for your help as always.
def neighbors
#neighbors ||= [-1, 0, 1].repeated_permutation(2).map do |dx, dy|
#grid[x + dx, y + dy] unless dx.zero? && dy.zero?
end.compact
end
I think I may have figured it out finally. The repeated_permutation(2) takes to of the values in the [-1,0,1] to search around the "cell" in question.
What ||= means is if #neighbors responds to a nil (NilClass) object type or false (FalseClass) object value, it will take the value which you're assigning in the right side, that's to say the result of:
[-1, 0, 1].repeated_permutation(2).map do |dx, dy|
#grid[x + dx, y + dy] unless dx.zero? && dy.zero?
end.compact
To use ||= is like to use x || x = a or maybe x = a unless x, but in the Ruby way to be simple to read, simple to understand, simple to work with.
And what the [-1, 0, 1].repeated_permutation(2).map is trying to do is to map the result of a repeated_permutation over the [-1, 0, 1] array and to take the first and second value within the permutation, and to set your #grid variable probably (because I can't say what's #grid) as a range starting in the sum of dx plus x and then dy plus y unless the value of dx and dy are 0 at the same time (&&). Then compact the "mapped" result.
You might want to see Array#repeated_permutation and Array#permutation
Quick & dirty permutations are like minor changes to a series of numbers ([link to more indepth])1. What you're looking at is the array with being altered by the .repeated_permutations to find all options for each value & the results then being added to the original x & y coords...
The ||= & the unless parts are just checks to ensure the code doesn't run on 0's ...

What are some nice ways to reverse a nested hash?

Suppose we have
b = {"b"=>"c"}
By doing b.invertwe can easily obtain the result of
{"c"=>"b"}
Thats when I thought of trying something pretty cool. Suppose we have
a = {"a"=>{"b"=>"c"}}
Whats a fairly efficient way to make this {{"c"=>"b"}=>"a"} (Here we reverse the most inner hash and work our way out)
Of course it would be best to extend this to n amount of hashes within each other. I've been looking for some other questions similar but haven't found any.
Thanks.
This can be accomplished with a recursive method for inverting the keys of the hash (and values, if desired). For example:
hsh = {{"c"=>"b"}=>"a"}
def recursive_invert(hsh)
hsh.each_with_object({}) do |(k, v), inverted_hsh|
if k.is_a? Hash
k = recursive_invert(k)
end
inverted_hsh[v] = k
end
end
recursive_invert(hsh) # {"a"=>{"b"=>"c"}}
Here's A recursive solution that will work in both directions.
def deep_invert(h)
h.each_with_object({}) do |(k,v),obj|
k = deep_invert(k) if k.is_a?(Hash)
v = deep_invert(v) if v.is_a?(Hash)
obj[v] = k
end
end
Example:
a = {"a"=>{"b"=>"c"}}
deep_invert(a)
#=> {{"c"=>"b"}=>"a"}
deep_invert(deep_invert(a)) == a
#=> true

All possible products

I'm trying to find all possible product of two 3-digit numbers. When I work with small ranges, I'm able to get an output in short amount of time but when the ranges are big, it seems to take really long time. Is there any way to to shorten the time to get the result?
The problem I'm working on is:
"A palindromic number reads the same both ways. The largest palindrome made from the product of two 2-digit numbers is 9009 = 91 × 99.
Find the largest palindrome made from the product of two 3-digit numbers."
a = []
for x in 100..999
for y in 100..999
num = (x * y)
unless a.include? num
a.push num
end
end
end
p a
This is going to compute 100 x 101 and 101 x 100 separately, even though they're not going to be pushed to the array since they're already in it.
I'm bad at math, but maybe every time x goes up, y's minimum range can go up since that one was just used? people who are better at math can tell me if this is going to start missing numbers.
z= 100
for x in 100..999
for y in z..999
num = (x * y)
unless a.include? num
a.push num
end
z = z+1
end
end
I think doing this might make the "unless a.include? num" line unnecessary, too.
Looking at your code a quick optimization you can make is to use a set rather than an array to store the already computed products.
Since a is an array, a.include?(num) will have to iterate through the entire list of elements before returning true / false.
If a were to be a set, a.include?(num) will return in sub linear time.
Example:
require 'set'
a = Set.new
for x in 100..999
for y in 100..999
num = (x * y)
unless a.include? num
a.add(num)
end
end
end
puts a.to_a.join(", ")
Moreover one of the nice properties of a set is that it only stores unique elements so the following would be equivalent:
require 'set'
a = Set.new
for x in 100..999
for y in 100..999
num = (x * y)
a.add(num)
end
end
puts a.to_a.join(", ")
What are you really trying to do, i.e. what is the original problem, and why do you need all of these products?
Are you printing every single one out? Is someone asking you for a concrete list of every single one?
If not, there is likely a better way to deal with this problem. For example, if all you wanted is to check if a number X will be an element in "that list of products", all you'd have to do is:
range = 100..999
range.any? { |i| range.include?(x / i) }

replacing 'eval' with a better solution

This method works, but it works very slowly. I think one of the problems might be the 'eval' statements in the add_by method.
Some explanation: every Node object has three relevant attributes, :x, :y and :neighbors. :x and :y are integers representing planar coordinates, :neighbors is an array, and nodes are stored in the #nodes array. The goal is to find, for each node k in #nodes, the nodes that are within some distance d of k and add them to the #neighbors array of k.
def set_neighbors d
def add_by dim, d
dict = {}
#nodes.each{|k| dict[k] = []}
#nodes.each_index do |k|
up = k+1
down = k-1
while up < #nodes.length and ((eval '#nodes[k].'+ dim) - (eval '#nodes[up].'+dim)).abs <= d
dict[#nodes[k]].push(#nodes[up])
up += 1
end
while down >= 0 and ((eval '#nodes[k].'+ dim) - (eval '#nodes[down].'+dim)).abs <= d
dict[#nodes[k]].push(#nodes[down])
down -= 1
end
end
return dict
end
#nodes.sort_by{|k| k.x}
exis = add_by('x', d)
whys = add_by('y', d)
#nodes.each do |k|
neighbors = exis[k]&whys[k]
k.neighbors = neighbors.select{|j| planar_distance(j,k) <= d}
end
end
My question is, how would you do this without either repeating the add_by routine for x and y or using eval?
You can avoid eval by using #nodes[k].send dim.to_sym.
I'm not sure what your code is doing exactly, but maybe a few pointers:
def set_neighbors d
#nodes.each do |node|
node.neighbors = #nodes.select do |n|
(node.x - n.x).abs <= d &&
(node.x - n.x).abs <= d &&
planar_distance(n,node) <= d
end - node
end
end
How would I do it? I'd use the Neo4J graph database via the neo4j gem (source). If you're concerned about performance, this is optimized for graph distance calculations. Also the API is very nice.
That being said, you really don't need eval at all. You can call a calculated method name on an object by using send. So you can replace the above with #nodes[k].send(dim), #nodes[up].send(dim), etc.

Resources