How to convert time to bigdecimal with ruby? - ruby

For example, I have a time data with string format:
00:25:23;16
I want to convert it to BigDecimal and tried:
a = '00:25:23;16'.to_d
=> #<BigDecimal:96cb548,'0.0',9(18)>
When I check a:
a.floor
=> 0
It looks not the true value. Then how to convert it the right way?
Addition
I expect the bigdecimal value like this(Maybe not a right value):
1543.123

Assuming the ;16 means milliseconds then maybe you are looking for this?
> str = "00:25:23;16"
=> "00:25:23;16"
> h, m, s, ms = str.split(/[:;]/).map(&:to_f)
=> [0.0, 25.0, 23.0, 16.0]
> h * 3600 + m * 60 + s + ms/1000
=> 1523.016

Related

Ruby : Convert String to Float

I have 3 string variables that I need to add.
a = "5.21", b= "5.22" and c = "5.23".
When I try to add i get a string, I need the numerical value
I have tried the following
a = a.to_f => 5.2
b = b.to_f => 5.2
c = c.to_f => 5.2
sum = a + b + c => 15.6
How do i get output 15.66. please help
Try taking advantage of Ruby's built in Enumerable methods. Try this:
a = "5.21"
b = "5.22"
c = "5.23"
[a, b, c].map(&:to_f).inject(:+)
#=> 15.66

Byte operations in Ruby

I would like to get a clue on how I can get Ruby to work with byte arrays
The below code is C#:
int t = (GetTime() / 60) //t is time in seconds divided by 60s (1 min)
byte[] myArray = new byte[64];
myArray[0] = (byte)(t >> 24);
myArray[1] = (byte)(t >> 16);
Any idea how I can get this to work in Ruby?
One way would be to work with arrays of integers and use Array#pack to pack the result into a binary string. E.g.
[65, 66, 67].pack('C*')
Returns ABC
Another way would be to manipulate a string directly when the encoding is set to "ASCII-8BIT"
Ruby can do bitwise operations and you can use a normal array, so I don't see a problem.
I don't use C# at the moment so I can't check if the results are the same.
t = Time.now.to_i / 60 #t is time in seconds divided by 60s (1 min)
myArray = []
myArray[0] = t >> 24
myArray[1] = t >> 16
p myArray #=>[1, 360]

Generate a GeoJSON heatmap using MongoDB map/reduce

I am visualising annual UK film screening using a javascript map that takes GeoJSON as an input. You can see it working for 2011 data here: http://screened2011.herokuapp.com
The generation of the GeoJSON is very inefficient - often taking 5+ seconds per "tile".
I have a Ruby app which queries MongoDB for a set of "screenings" within a bounding box (as requested by the JS) and then generates a two-dimensional array representing the total number of screenings that occurred within each of a 16x16. This is apparently the bottleneck - it's hitting the server and pulling down all of these screenings.
I'd like to replace this with a map/reduce query that aggregates the count of all screenings within a bounding box into a 16x16 array of values, but I'm not having much success - it's quite the task for my first map/reduce!
Here's a simplified version of my code with unrelated stuff taken out (it's awful, and if this weren't a hack coming to an end, I'd refactor):
get :boxed, :map => "/screenings/year/:year/quadrant/:area/bbox/:bbox", :provides => [:json, :jsonp], :cache => false do
box = parameter_box(params[:bbox]) # returns [[minx,miny],[maxx,maxy]]
year = params[:year].to_i
screenings = Screening.where(:location.within(:box) => box).where(:year => year)
jsonp Screening.heatmap(screenings, box, 16)
end
def self.heatmap screenings, box, scale
grid = []
min_x = box[0][0]
min_y = box[0][1]
max_x = box[1][0]
max_y = box[1][1]
box_width = max_x.to_f - min_x.to_f
box_height = max_y.to_f - min_y.to_f
return [] if box_width == 0 || box_height == 0
# Set up an empty GeoJSON-style array to hold the results
scalef = scale.to_f
(0..(scale - 1)).each do |i|
grid[i] = []
(0..(scale - 1)).each do |j|
box_min_x = min_x + (i * ( box_width / scalef ))
box_max_x = min_x + ((i + 1) * ( box_width / scalef ))
box_min_y = min_y + (j * ( box_height / scalef ))
box_max_y = min_y + ((j + 1) * ( box_height / scalef ))
grid[i][j] = {
:count => 0,
#:id => "#{box_min_x}_#{box_max_x}_#{box_min_y}_#{box_max_y}",
:coordinates => [
[
[box_min_x,box_min_y], [box_min_x, box_max_y], [box_max_x, box_max_y], [box_max_x, box_min_y], [box_min_x,box_min_y]
]
]
}
end
end
# This loop is the bottleneck and I'd like to replace with a map-reduce
screenings.only(:location, :total_showings).each do |screening|
x = (scale * ((screening.location[0] - min_x) / box_width)).floor
y = (scale * ((screening.location[1] - min_y) / box_height)).floor
raise if x > (scale - 1)
raise if y > (scale - 1)
grid[x][y][:count] += screening.total_showings.to_i
end
# This converts the resulting 16x16 into GeoJSON
places = []
grid.each do |x|
x.each do |p|
if p[:count].to_i > 0
properties = {}
properties[:total_showings] = p[:count]
places << {
"id" => p[:id],
"type" => "Feature",
"geometry" => {
"type" => "Polygon",
"coordinates" => p[:coordinates]
},
"properties"=> properties
}
end
end
end
{
"type" => "FeatureCollection",
"features" => places
}
end
I'm using Mongoid, so I could chain a mapreduce onto the screenings query, and I'm hoping that this would greatly speed up the process - but how should I go about getting something like the following to pass into this function?:
[
[1,20000,30,3424,53,66,7586,54543,76764,4322,7664,43242,43,435,32,643],
...
]
...based on several million records each in this structure (essentially summing the total_showings) for each one within a bounding box:
{"_id"=>BSON::ObjectId('50e481e653e6dfbc92057e8d'),
"created_at"=>2013-01-02 18:52:22 +0000,
"ended_at"=>Thu, 07 Jun 2012 00:00:00 +0100,
"events"=>["10044735484"],
"film_id"=>BSON::ObjectId('4f96a91153e6df5ebc001afe'),
"genre_ids"=>[],
"location"=>[-2.003309596016, 52.396317185921],
"performance_id"=>"9001923080",
"specialised"=>false,
"started_at"=>Fri, 01 Jun 2012 00:00:00 +0100,
"total_showings"=>1,
"updated_at"=>2013-01-02 18:52:22 +0000,
"venue_id"=>BSON::ObjectId('4f9500bf53e6df004000034d'),
"week"=>nil,
"year"=>2012}
Thanks in advance folks!

math calculations in ruby

I have a standard formula for calculating loan amortization schedule.
Here is the formula:
(Px(i/12))/(1-(1+i/12)^-n)
Here is what I have in ruby:
p = BigDecimal('1000.0')
n = BigDecimal('12')
i = BigDecimal('3')
m = (p * (i/12))/(1-(1+i/12) ** -n)
I'm getting a following error: in `**': wrong argument type BigDecimal (expected Fixnum) (TypeError)
I'm having hard time trying to play with Fixnun, Float and BigDecimal in ruby.
n must be an integer per definition if you want to use ** aka BigDecimal#power(n)
PS: I just looked up your formula on wikipedia. Since n is the number of payments, it will be an integer by nature so just use a Fixnum for n - you won't get any troubles :)
Using floats :
>> p = 1000.0
=> 1000.0
>> n = 12.0
=> 12.0
>> i = 3.0
=> 3.0
>> (p*(i/12))/(1-(1+i/12)**-n)
=> 268.447577024146
Using BigDecimal :
>> p = BigDecimal('1000.0')
=> #<BigDecimal:1082c4760,'0.1E4',4(12)>
>> n = BigDecimal('12')
=> #<BigDecimal:1082c14c0,'0.12E2',4(8)>
>> i = BigDecimal('3')
=> #<BigDecimal:1082be2e8,'0.3E1',4(8)>
>> m = (p * (i/12))/(1-(1+i/12) ** -n.to_i)
=> #<BigDecimal:1082b4950,'0.2684475770 2414639639 7495957671 887300036E3',40(48)>
>> m.to_i
=> 268
It seems that the BigDecimal ** only takes a FixNum as the power part...
By putting the .0 at the end of the numbers it makes them all into floats. Works for me.

Parsing latitude and longitude with Ruby

I need to parse some user submitted strings containing latitudes and longitudes, under Ruby.
The result should be given in a double
Example:
08º 04' 49'' 09º 13' 12''
Result:
8.080278 9.22
I've looked to both Geokit and GeoRuby but haven't found a solution. Any hint?
"08° 04' 49'' 09° 13' 12''".gsub(/(\d+)° (\d+)' (\d+)''/) do
$1.to_f + $2.to_f/60 + $3.to_f/3600
end
#=> "8.08027777777778 9.22"
Edit: or to get the result as an array of floats:
"08° 04' 49'' 09° 13' 12''".scan(/(\d+)° (\d+)' (\d+)''/).map do |d,m,s|
d.to_f + m.to_f/60 + s.to_f/3600
end
#=> [8.08027777777778, 9.22]
How about using a regular expression? Eg:
def latlong(dms_pair)
match = dms_pair.match(/(\d\d)º (\d\d)' (\d\d)'' (\d\d)º (\d\d)' (\d\d)''/)
latitude = match[1].to_f + match[2].to_f / 60 + match[3].to_f / 3600
longitude = match[4].to_f + match[5].to_f / 60 + match[6].to_f / 3600
{:latitude=>latitude, :longitude=>longitude}
end
Here's a more complex version that copes with negative coordinates:
def dms_to_degrees(d, m, s)
degrees = d
fractional = m / 60 + s / 3600
if d > 0
degrees + fractional
else
degrees - fractional
end
end
def latlong(dms_pair)
match = dms_pair.match(/(-?\d+)º (\d+)' (\d+)'' (-?\d+)º (\d+)' (\d+)''/)
latitude = dms_to_degrees(*match[1..3].map {|x| x.to_f})
longitude = dms_to_degrees(*match[4..6].map {|x| x.to_f})
{:latitude=>latitude, :longitude=>longitude}
end

Resources