Ruby on Rails query returns extra column - ruby-on-rails

I am working with Ruby 2.0.0 and Rails 4.0.9, on Oracle 11g database.
I query the database to get pairs of values [date, score] to draw a chart.
Unfortunately, my query returns triplets such as [date, score, something], and the chart fails.
Here is the query:
#business_process_history = DmMeasure.where("period_id between ? and ? and ODQ_object_id = ?",
first_period_id, current_period_id, "BP-#{#business_process.id}").
select("period_day, score").order("period_id")
Here is the result in the console:
DmMeasure Load (1.2ms) SELECT period_day, score FROM "DM_MEASURES" WHERE (period_id between 1684 and 1694 and ODQ_object_id = 'BP-147') ORDER BY period_id
=> #<ActiveRecord::Relation [#<DmMeasure period_day: "20140811", score: #<BigDecimal:54fabf0,'0.997E2',18(45)>>,
#<DmMeasure period_day: "20140812", score: #<BigDecimal:54fa7e0,'0.997E2',18(45)>>, ...]
Trying to format the result also returns triplets:
#business_process_history.map { |bp| [bp.period_day, bp.score] }
=> [["20140811", #<BigDecimal:54fabf0,'0.997E2',18(45)>],
["20140812", #<BigDecimal:54fa7e0,'0.997E2',18(45)>], ...]
Where does this come from?
How can I avoid this behaviour?
Thanks for your help,
Best regards,
Fred

what triplets? From what I can see, you have two attributes per item: 'period_day' (a string representing a date) and 'score' (a BigDecimal representation of a single number).
The ruby BigDecimal is just one way of representing a number.
eg if you play around with them in the rails console:
> BigDecimal.new(1234)
=> #<BigDecimal:f40c6d0,'0.1234E4',9(36)>
The first part, as you can see, a bit like scientific notation, it contains the significant digits and precision.
To figure out what the 18(45) is, I had to dig into the original c-code for the BigDecimal#inspect method.
Here's the commenting for that method:
/* Returns debugging information about the value as a string of comma-separated
* values in angle brackets with a leading #:
*
* BigDecimal.new("1234.5678").inspect ->
* "#<BigDecimal:b7ea1130,'0.12345678E4',8(12)>"
*
* The first part is the address, the second is the value as a string, and
* the final part ss(mm) is the current number of significant digits and the
* maximum number of significant digits, respectively.
*/
The answer being: the current number of significant digits and the maximum number of significant digits, respectively

Related

Sum of table attributes when value is string

I've never came across this before. I'm working with a table attribute whos value is a string, not float/int.
Model.first.amount => "58.00"
I need to sum up all amount. What I'm used to, with the amount being a float, would be:
Model.all.sum(&:amount) => # total value
Took a wild guess with:
Model.all.sum(&:amount.to_i) # undefined method `to_i' for :amount:Symbol
Is there a clean way to sum up the amount? Or convert the database to float?
Processing database with Ruby is memory inefficient.
First shot:
Model
.pluck(:amount) # will fire sql
.sum(&:to_f) # convert to float, operating on resulting Array, not AR and sum
But the most effective way to process database data is SQL of course:
Model.sum("CAST(COALESCE(amount, '0') AS DECIMAL)")
coalesce will replace null values with '0'
sum all values casted to DECIMAL.
In pure Ruby you can use method inject.
Model.all.inject(0) { |sum, object| sum += object.amount.to_i }
I dont have commenting permissions but this should work for ruby:
Model.all.map(&:to_f).reduce(&:+)

syntax error, unexpected '}', expecting '='

otherCount = #products.drop(3).inject(0) { |sum,count| sum, count }
My Ruby environment is 1.9.3.
products is an array of hashes elements. It has properties: productName and count. I want to sum up the count values of all the hashes in the products array (with the exception of the first 3 hashes). Documentation I've found are either too brief in their explanation or use a different Ruby environment, which may likely be the problem. The code I wrote is written as per this document.
I drop the first 3 elements, then call inject, with an initial value of 0, carry over variable called sum, and count is the name of the field in each of the hashes whose value I want to add up.
Change
inject(0) { |sum,count| sum, count }
to
inject(0) { |sum,p| sum + p['count'] }
Isolate the code
If you're having trouble integrating this, copy and paste these 2 lines into an irb session to verify this works:
a = [{'count' => 1},{'count' => 2},{'count' => 3}]
a.inject(0) { |sum,p| sum + p['count'] }
# => 6
Hopefully this helps bridge the gap.

RoR sum column of datetime type

I'm calculating total "walk time" for dog walking app. The Walks table has two cols, start_time and end_time. Since I want to display total time out for ALL walks for a particular dog, I should just be able to sum the two columns, subtract end_times_total from start_time_totals and result will be my total time out. However I'm getting strange results. When I sum the columns thusly,
start_times = dog.walks.sum('start_time')
end_times = dog.walks.sum('end_time')
BOTH start_times and end_times return the same value. Doing a sanity check I see that my start and end times in the db are indeed set as I would expect them to be (start times in the morning, end times in the afternoon), so the sum should definitely return a different value for each of the columns. Additionally, the value is different for each dog and in line with the relative values I would expect, so dogs with more walks return larger values than dogs with fewer walks. So, it looks like the sum is probably working, only somehow returning the same value for each column.
Btw, running this in dev Rails 3.2.3, ruby 2.0, SQLite.
Don't think that summing datetimes is a good idea. What you need is calculate duration of each single walk and sum them. You can do it in 2 ways:
1. DB-dependent, but more efficient:
# sqlite in dev and test modes
sql = "strftime('%s',end_time) - strftime('%s',start_time)" if !Rails.env.production?
# production with postgres
sql = "extract(epoch from end_time - start_time)" if Rails.env.production?
total = dog.walks.sum(sql)
2. DB-agnostic, but less efficient in case of hundreds record for each dog:
total = dog.walks.all.inject(0) {|tot,w| tot+=w.end_time-w.start_time}
I don't know, how sqlite handles datetime and operations on this data type, but while playing in sqlite console, I noticed that I could get reliable effects when converting datetime to seconds.
I would write it like:
dog.walks.sum("strftime('%s', end_time) - strftime('%s', start_time)")
Query should look like:
select sum(strftime('%s', end_time) - strftime('%s', start_time)) from walks;

Weighted random pick from array in ruby/rails

I have a model in Rails from which I want to pick a random entry.
So far I've done it with a named scope like this:
named_scope :random, lambda { { :order=>'RAND()', :limit => 1 } }
But now I've added an integer field 'weight' to the model representing the probability with which each row should be picked.
How can I now do a weighted random pick?
I've found and tried out two methods on snippets.dzone.com that extended the Array class and add a weighted random function, but both didn't work or pick random items for me.
I'm using REE 1.8.7 and Rails 2.3.
Maybe I understand this totally wrong, but couldn't you just use the column "weight" as a factor to the random number? (Depending on the Db, some precautions would be necessary to prevent the product from overflowing.)
named_scope :random, lambda { { :order=>'RAND()*weight', :limit => 1 } }
In one query you should:
calculate the total weight
multiply by a random factor, giving a weight threshold
scan again through the table summing, until the weight threshold is reached.
In SQL it would be sompething like this (not tried for real)
SELECT SUM(weight) FROM table INTO #totalwt;
#lim := FLOOR(RAND() * #totalwt);
SELECT id, weight, #total := #total + weight AS cumulativeWeight
FROM table WHERE cumulativeWeight < #lim, (SELECT #total:=0) AS t;
Inspired by Optimal query to fetch a cumulative sum in MySQL

How to identify a country from a normalized phone number?

I have a list of international phone numbers and a List of Country calling codes.
I would like to identify the Country from the numbers but I can't find a fast and elegant way to do it.
Any idea? The only I got is to have an hardcoded check (Eg. "look at the first number, look at the second number: if it's X then check for the third number. If the second number is Y then the Country is Foo", etc.).
I'm using PHP and a DB (MySQL) for the lists, but I think that any pseudocode will help.
Alternatively, you could use a tool like Twilio Lookup.
The CountryCode property is always returned when you make an API request with Lookup.
https://www.twilio.com/docs/api/lookups#lookups-instance-properties
[Disclosure: I work for Twilio]
i was after something similar to this, but i also wanted to determine the region/state - if available. in the end i hacked up something based on a tree of the digits leading digits (spurred on by the description at wikipedia)
my implementation is available as a gist.
I'm currently using an implementation of Google's libphonenumber in Node, which works fairly well. I suppose you could try a PHP implementation, e.g. libphonenumber-for-php.
The hard-coded check can be turned into a decision tree generated automatically from the list of calling codes. Each node of the tree defines the 'current' character, the list of possible following characters (tree nodes) or a country in case it's a terminal node. The root node will be for the leading '+' sign.
The challenge here is that some countries share the same phone country code. E.g. both Canada and the US have phone numbers starting with +1.
I'm using https://github.com/giggsey/libphonenumber-for-php as following:
/**
* Get country
* #param string $phone
* #param string $defaultCountry
* #return string Country code, e.g. 'CA', 'US', 'DE', ...
*/
public static function getCountry($phone, $defaultCountry) {
try {
$PhoneNumberUtil = \libphonenumber\PhoneNumberUtil::getInstance();
$PhoneNumber = $PhoneNumberUtil->parse($phone, $defaultCountry);
$country = $PhoneNumberUtil->getRegionCodeForNumber($PhoneNumber);
return $country;
} catch (\libphonenumber\NumberParseException $e) {
}
return $defaultCountry;
}
You can easily do a simple lookup starting with the first number, then the second, and so on until you find it. This will work correctly because no calling code is a prefix of another code, i.e. the international calling codes form a "prefix code" (the phone system relies on this property).
I'm not good any good at PHP so here is a simple python implementation; hopefully it is easy to follow:
>>> phone_numbers = ["+12345", "+23456", "+34567", "+45678"]
>>> country_codes = { "+1": "USA", "+234": "Nigeria", "+34" : "Spain" }
>>> for number in phone_numbers:
... for i in [2, 3, 4]:
... if number[:i] in country_codes:
... print country_codes[number[:i]]
... break
... else:
... print "Unknown"
...
USA
Nigeria
Spain
Unknown
Essentially you have an associative array between prefixes and countries (which I assume you can easily generate from that Wikipedia article. You try looking up the first digit of the phone number in the associative array. If it's not in the array you try the first two digits, then the first three. If there is no match after three digits then this number doesn't start with a valid international calling code.

Resources