Reputation: 721
I have a table of consumptions
create_table "consumptions", force: :cascade do |t|
t.float "kilometers"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "user_id"
end
Here is a list of consumptions for user 1
#<Consumption:0x007f8654895080
id: 1,
kilometers: 10000.0,
created_at: Wed, 07 Nov 2018 15:23:21 UTC +00:00,
updated_at: Wed, 07 Nov 2018 15:23:21 UTC +00:00,
user_id: 1>,
#<Consumption:0x007f865489c268
id: 4,
kilometers: 10800.0,
created_at: Wed, 24 Oct 2018 14:30:23 UTC +00:00,
updated_at: Thu, 08 Nov 2018 12:35:37 UTC +00:00,
user_id: 1>,
#<Consumption:0x007f8654893f50
id: 6,
kilometers: 11400.0,
created_at: Thu, 08 Nov 2018 11:56:52 UTC +00:00,
updated_at: Thu, 08 Nov 2018 12:36:42 UTC +00:00,
user_id: 1>,
I want to compare the difference of kilometers between each consumption...
consumptions
id | km |
1 | 10000 |
4 | 10800 |
6 | 11400 |
Result between id 1 and 4 should be 800
Result between 4 and 6 should be 600
Upvotes: 0
Views: 51
Reputation: 721
I found an easier way:
So I added an attribute difference
set by default to 0
to consumptions
class Consumption < ApplicationRecord
belongs_to :user
before_save :count_difference
def count_difference
if Consumption.any?
if Consumption.last.user_id == self.user_id
self.difference = Consumption.last.kilometers - self.kilometers
end
end
end
end
Upvotes: 0
Reputation: 6455
To give more of a 'ruby' answer:
def difference_in_km(obj_1, obj_2)
(obj1.kilometers - obj2.kilometers).abs
end
The abs is there to handle negative figures, returning 400 instead of -400 for example.
This lets you do:
con_1 = Consumption.find(1)
con_2 = Consumption.find(4)
difference_in_km(con_1, con_2)
Another approach would be to add an instance method to your Consumption class:
class Consumption < ApplicationRecord
....
....
def distance_from(other_consumption)
(kilometers - other_consumption.kilometers).abs
end
end
This will allow you to do:
con_1.distance_from(con_2)
Upvotes: 0
Reputation: 58
I would recommend you to take a look at https://github.com/geokit/geokit-rails
You just need to add acts_as_mappable
to your model (decide if you config your default values or place them in your model, I think the best would be to do it on a config file).
Then you just use by_distance
method and your code would look so much nice :)
Upvotes: 1
Reputation: 2144
What database are you using?
Basically, you want to use a window function:
E.g. for PostgreSQL:
select id, kilometers - lag(kilometers) over (order by id) as difference? from consumptions;
You could then use find_by_sql to retrieve the value as an ActiveRecord model, suggested here.
The results will be returned as an array with columns requested encapsulated as attributes of the model you call this method from.
I haven't tried it but yours would look like:
Consumptions.find_by_sql('select id, kilometers - lag(kilometers) over (order by id) as difference? from consumptions')
Upvotes: 0