Ruby Fixnum comparison - ruby-on-rails

After gathering data from a web service, I'm trying to pull out only those records that have a value for a particular field > 0. The field that I'm checking is a FixNum field. I have tried multiple things like .nil? or > 0 etc. But none of them seem to work.
Here is how the code goes:
results.each do |r|
# puts "came"
if r['efforRemaining'] % 1 != 0 #i tried r['efforRemaining'].nil? ..
# i have values like 0.4, 0.5 etc for the field, hence i need to check if the value is greater than 0 or not only
puts "came!"
puts "#{r['mstone']} ... #{r['assignee']['id']} ... #{r['effortRemaining'].to_f}"
end
end
Could somebody let me know how to do this?
thanks,

If puts r['effortRemaining'].inspect comes out in quote marks, that means it is a string and you need to convert it to a number before you compare it.
This should work:
if r['effortRemaining'].to_f > 0

Related

How to iterate over an ActiveRecord resultset in one line with nil check in Ruby

I have an array of Active Record result and I want to iterate over each record to get a specific attribute and add all of them in one line with a nil check. Here is what I got so far
def total_cost(cost_rec)
total= 0.0
unless cost_rec.nil?
cost_rec.each { |c| total += c.cost }
end
total
end
Is there an elegant way to do the same thing in one line?
You could combine safe-navigation (to "hide" the nil check), summation inside the database (to avoid pulling a bunch of data out of the database that you don't need), and a #to_f call to hide the final nil check:
cost_rec&.sum(:cost).to_f
If the cost is an integer, then:
cost_rec&.sum(:cost).to_i
and if cost is a numeric inside the database and you don't want to worry about precision issues:
cost_rec&.sum(:cost).to_d
If cost_rec is an array rather than a relation (i.e. you've already pulled all the data out of the database), then one of:
cost_rec&.sum(&:cost).to_f
cost_rec&.sum(&:cost).to_i
cost_rec&.sum(&:cost).to_d
depending on what type cost is.
You could also use Kernel#Array to ignore nils (since Array(nil) is []) and ignore the difference between arrays and ActiveRecord relations (since #Array calls #to_ary and relations respond to that) and say:
Array(cost_rec).sum(&:cost)
that'll even allow cost_rec to be a single model instance. This also bypasses the need for the final #to_X call since [].sum is 0. The downside of this approach is that you can't push the summation into the database when cost_rec is a relation.
anything like these?
def total_cost(cost_rec)
(cost_rec || []).inject(0) { |memo, c| memo + c.cost }
end
or
def total_cost(cost_rec)
(cost_rec || []).sum(&:cost)
end
Either one of these should work
total = cost_rec.map(&:cost).compact.sum
total = cost_rec.map{|c| c.cost }.compact.sum
total = cost_rec.pluck(:cost).compact.sum
Edit: if cost_rec is nil
total = (cost_rec || []).map{|c| c.cost }.compact.sum
When cost_rec is an ActiveRecord::Relatation then this should work out of the box:
cost_rec.sum(:cost)
See ActiveRecord::Calculations#sum.

Count of a count with Rails

Using Usage.group(:song_id), I can get all the usages of any particular song in my app. Using Usage.group(:song_id).count, I can get a hash like {song_id => usage_count ...}.
How do I produce a count of that count though? i.e something like this:
[
used_once: number_of_songs_used_once,
used_twice: number_of_songs_used_twice,
used_thrice: number_of_songs_used_thrice,
etc.
]
(Okay, so really I would expect output to look something like {1=>14, 2=>6, 3=>2, 4=>1}).
You can use inject on the hash.
song_values = Usage.group(:song_id).count.values
times_used = song_values.inject({}) do |used, count|
if used[count].nil?
used[count] = 1
else
used[count] += 1
end
used
end
You could use a ternary operator if you want the if/else to be one line
used[count].nil? ? used[count] = 1 : used[count] += 1
This is just looping over the songs usage count building a hash where the key is the number of times that song was used, updating the value for each count (not explained that particularly well but I hope you understand).

Rails 4 where,order,group,count include zero's - postgresql

Here is my query:
User.where("created_at >= ? AND created_at <=?", date1,date2).order('DATE(created_at) DESC').group("DATE(created_at)").count
and I get output as:
{Thu, 15 May 2014=>1}
But I want to get output as 0 for the rest of the days. For ex
{Thu, 15 May 2014=>1,Fri, 15 May 2014=>0}
What I want to get is Users created in a date range, ordered and grouped by created_at and number of such Users for each day. When no users are there for a particular day it should return 0, which the current query doesn't return.
I agree with Himesh
Try this:
User.where("created_at >= ? AND created_at <=?", date1,date2).order('DATE(created_at) DESC').group("DATE(created_at)").count
Which will give:
{Thu, 15 May 2014=>1}
Get the date range as hash initialized to 0:
hash1 = Hash[(date1..date2).collect { |v| [v, 0] }]
Now merge the two hashes:
hash = hash1.merge(hash)
Do remember to merge hash with hash1 and not the vice-versa, because we want to overwrite the value of keys from hash to hash1
Although it doesn’t seem to be well known, Ruby hashes have a “default value” feature that will help you do what you want in a better way. Another way of expressing what you’re trying to do is this:
Create a hash that gives 0 for any key within my data range unless I have stored some other value under that key.
You can accomplish exactly this using a block to establish the default value of the hash:
#data = Hash.new do |h, key|
if (date1..date2).include?(key)
0
else
nil
end
end
Or, more succinctly:
#data = Hash.new {|h, key| (date1..date2).include?(key) ? 0 : nil }
Then load your data from the database and merge it into this hash:
#data.merge! User.where(…).group(…).count
Note: Your order() is irrelevant because you are grouping. It will probably get optimized away, but you can simply leave it off and get the same result.
Now if you do #data[foo]:
if foo is not comparable to a date you will get nil
if foo is outside the date range you will get nil
if foo is within the date range but no data was returned by your query you will get 0
if foo is a date with data in the database you will get the correct count
This method is preferable to pre-loading the hash because it will work efficiently (in time and space) even for very large date ranges. And arguably it is more straightforward too.
I created a gem exactly for this - check out Groupdate.
Perhaps, this is a bit difficult as if any user is not created on 16th May then there would be no record of 16th May as created_at in the DB and group by query result wont contain any value for 16th May.
Probably you will have to handle this in ruby. Or what you can do is check if a particular date is present as key in the hash_result, and if it is not present then user count is 0 by default.
Hope this helps :)

Ruby on Rails: Failing to convert value in hash to float

I feel like I'm missing something really obvious here.
I have the following hash placed in variable "simple_lable". Calling inspect on it works fine, but I cannot seem to access any of the numerical values. .to_i comes out as 0, .to_f comes out as 0.0 and to_s comes out blank
puts "**************************"
puts simple_label.inspect
puts simple_label["margin_top"].to_f
puts simple_label["margin_bottom"].to_f
puts simple_label["margin_right"].to_f
puts simple_label["margin_left"].to_f
puts simple_label["paper_size"]
puts "**************************"
results in
**************************
{"paper_size"=>"LETTER", "top_margin"=>36, "bottom_margin"=>36, "left_margin"=>15.822, "right_margin"=>15.822, "columns"=>3, "rows"=>10, "column_gutter"=>15, "row_gutter"=>0}
0.0
0.0
0.0
0.0
LETTER
**************************
So the values are there, but I just can't seem to get hold of them correctly.
Any ideas?
Many thanks.
The problem is that your hash keys are incorrect. Your hash has, for example, top_margin, as a key, but you are trying to reference a key of margin_top. The hash will return nil for a non-existent key, and to_f, to_i are giving zero, and to_s giving blank for a nil hash value.

activerecord sum returns a string?

This seems very strange to me, an active record sum returns a string, not a number
basket_items.sum("price")
This seems to make it work, but i thought i may have missed something, as this seems like very strange behaviour.
basket_items.sum("price").to_i
According to the (rails 2.3.9) API:
The value is returned with the same data type of the column, 0 if there’s no row
Could your price column be a string or text?
https://github.com/rails/rails/pull/7439
There was a reason it returned a string - calling to_d on a Fixnum in Ruby 1.8 would give a NoMethodError. This is no longer the case in Ruby 1.9 so it's probably okay to change.
ActiveRecord sum:
Difference:
1) basket_items.sum("price")
It will also sum non integer also and it will return non integer type.
2) basket_items.sum("price").to_i
This above will convert into integer.
# File activerecord/lib/active_record/relation/calculations.rb, line 92
def sum(*args)
if block_given?
self.to_a.sum(*args) {|*block_args| yield(*block_args)}
else
calculate(:sum, *args)
end
end
Calculates the sum of values on a given column. The value is returned with the same data type of the column, 0 if there’s no row.
http://api.rubyonrails.org/classes/ActiveRecord/Calculations.html#method-i-sum
Github:
https://github.com/rails/rails/blob/f8f4ac91203506c94d547ee0ef530bd60faf97ed/activerecord/lib/active_record/relation/calculations.rb#L92
Also see, Advanced sum() usage in Rails.

Resources