Sort records into buckets with Rails - ruby-on-rails

I have a Game model that has a datetime, storing the date and time the game will be played. I'd like to put those records into an array of arrays of each date.
Example of a possible outcome array:
[
[
#<Game id: 7, datetime: "2014-03-03 05:30:00">,
#<Game id: 12, datetime: "2014-03-03 11:30:00">,
#<Game id: 14, datetime: "2014-03-03 12:30:00">,
#<Game id: 17, datetime: "2014-03-03 12:45:00">
],
[
#<Game id: 1, datetime: "2014-04-06 05:30:00">,
#<Game id: 2, datetime: "2014-04-06 11:30:00">,
#<Game id: 4, datetime: "2014-04-06 12:30:00">,
#<Game id: 7, datetime: "2014-04-06 12:45:00">
]
]
Here's what I'm trying so far
#games = Array.new
Game.all.each do |game|
# in my model I have a function date that gets the date part of my datetime
quit = false
index = 0
while !quit do
if index < #games.length
quit = true
#games << [game]
elsif #games[index].first.date == game.date
quit = true
#games[index] << game
end
end
end
But this seems not very ruby-like and probably not the most efficient way. Is there a better approach for this?

This is probably not the most efficient way to do this, but it is short and readable:
#games = {}
Game.all.each do |game|
#games[game.date] = [] if !#games.has_key? game.date
#games[game.date] << game
end
#games = #games.values
Note that it is idiomatic Ruby to use array and hash literals [] and {} instead of their respective Array.new and Hash.new constructors.
Update
Here's an even better way, using the group_by method of Enumerable, which collections can include as a module:
#games = Game.all.group_by { |game| game.date }
Note that this will return a hash using the dates as keys, but you can easily turn this into an array by calling the values method on a hash.

Related

Ruby - how to effectively fetch and group certain information from an object?

I have an object containing tens of thousands orders. The object looks like this:
[#<Order id: 4304, order_code: 'AB11', manufacturer_id: 24, ...>,
#<Order id: 7664, order_code: 'AB13', manufacturer_id: 24, ...>,
#<Order id: 4164, order_code: 'AB255', manufacturer_id: 724, ...>,
#<Order id: 7671, order_code: 'AX88', manufacturer_id: 855, ...>,
...]
Now, what I am trying to get - I'd like to get an object looking like this (or something similar):
[
{manufacturer_id: 24, orders: [{id: 4304, order_code: 'AB11'}, {id: 7664, order_code: 'AB13'}]},
{manufacturer_id: 724, orders: [{id: 4164, order_code: 'AB255'}]},
...
]
What is the most effective way to extract that information from the given Order object? This will be pulled off couple times a day.
Thank you.
You can do something like this
list.group_by(&:manufacturer_id).map do |k,v|
{
manufacturer_id: k,
orders: v.collect{ |o| {id: o.id, order_code: o.order_code }}
}
end
output look like below
=> [{:manufacturer_id=>24, :orders=>[{:id=>4304, :order_code=>"AB11"}, {:id=>7664, :order_code=>"AB13"}]}, {:manufacturer_id=>724, :orders=>[{:id=>4164, :order_code=>"AB255"}]}, {:manufacturer_id=>855, :orders=>[{:id=>7671, :order_code=>"AX88"}]}]
I would end up some presenter, something like PORO (plain old ruby object)
class OrderPresenter
attr_reader :records
def initialize(records)
#records = records
end
def as_json
records.group_by(&:manufacturer_id).map do |k, v|
{
manufacturer_id: k,
orders: v.collect { |o| {id: o.id, order_code: o.order_code } }
}
end
end
end
And I don't think you need them all at once?
So you can do something like partial load, not to blow up all your memory
records.each_slice(1000) do |group|
data = OrderPresenter.new(records).as_json
# perform something on that data
end

Move one object in collection of objects in rails

Here is the scenario, I have these objects. Let's assume that this is a User:
The object came from:
#user = User.all
User Object
[<#User id: 1, firstname: "John", lastname: "Pond">,<#User id: 2, firstname: "Paul", lastname: "Rich">,<#User id: 3, firstname: "Jasmine", lastname: "Ong">]
How can I move one object up, for example I want to move User.id == 2? The result I want is shown below.
[<#User id: 2, firstname: "Paul", lastname: "Rich">,<#User id: 1, firstname: "John", lastname: "Pond">,<#User id: 3, firstname: "Jasmine", lastname: "Ong">]
I already got the answer. Here is what I made to made my question above worked.
#users = User.all
user_ids = User.pluck(:id)
user_ids.delete(2)
new_user_ids = [2]
user_ids.each do |id|
new_user_ids << id
end
#users.sort_by { |user| new_user_ids.index(user.id) }
And this made perfect!
We can also do it in a way like this:
Add a new method to Array. lib/rails_extensions.rb
class Array
def swap!(a, b = a - 1)
self[a], self[b] = self[b], self[a]
self
end
end
Then add this in config/environment.rb
require 'rails_extensions'
So we can use the method swap! for arrays and it will swap the object with the one before it. We can do something like this:
#users = User.all #[<#User id: 1>, <#User id: 2>]
user_id = #users.rindex {|user| user.id == 2}
#users = #users.swap!(user_id) #[<#User id: 2>, <#User id: 1>]
is this too ugly?
hash = [{ id: 1}, {id: 2}, {id: 3}]
hash.unshift(hash.delete(hash.select {|h| h[:id] == 2 }.first))
=> [{:id=>2}, {:id=>1}, {:id=>3}]

Array of hashes where hash is returned by a function in jbuilder

I have a PORO TutorProfileHandler that has a function json that returns a hash.
class TutorProfileHandler
def initialize(opts)
#profile = opts[:tutor_profile]
end
def json
tutor = #profile.tutor
return {
id: tutor.id,
first_name: tutor.first_name,
last_name: tutor.last_name.first + '.',
school: #profile.school,
avatar: #profile.avatar.url,
bio: #profile.bio,
academic_level: #profile.academic_level,
headline: #profile.headline,
major: #profile.major,
rate: #profile.rate,
rating: #profile.rating,
courses: JSON.parse(#profile.courses),
video_url: #profile.video_url
}
end
end
In my index_tutor_profiles.json.jbuilder, I would like to generate
{
tutor_profile: [{id: 1, ...}, {id: 2, ...}, ...],
tutor_sum: 20
}
However when I do this
json.tutor_profiles (#tutor_profiles) do |profile|
TutorProfileHandler.new({tutor_profile: profile}).json
end
json.tutor_sum #tutor_sum
It gives me an empty array for tutor_profiles.
However if I move everything from TutorProfileHandler.json to the jbuilder file, it works. How do I explicitly include the hash returned by TutorProfileHandler.json in the jbuilder array?
Note: This returns an array, but it creates a new key-value pair array:
json.tutor_profiles json.array(#tutor_profiles) do |profile|
TutorProfileHandler.new({tutor_profile: profile}).json
end
Result:
{
array: [{id: 1, ...}, {id: 2, ...}, ...],
tutor_profile: [],
tutor_sum: 20
}
There is a ugly approach:
json.tutor_profiles #tutor_profiles do |profile|
tmp_json = TutorProfileHandler.new({tutor_profile: profile}).json
json.(tmp_json, *(tmp_json.keys))
end
I think the best practise is directly nesting inside model. You can get more information from the its github page.

Grouping by attribute of associated objects

class Event < ActiveRecord::Base
has_many :meetings
end
class Meeting < ActiveRecord::Base
belongs_to :event
end
How to write mysql query to search all events group_by meeting DATE(start_at)?
Event.inludes(:meetings).group ...
As a result I want to get a Hash:
{"2014-01-24"=>[#<Event id: , title: "First", created_at: "2014-01-24 16:02:52", updated_at: "2014-01-24 16:02:52">, #<Event id: 2, title: "Second", created_at: "2014-01-24 16:02:52", updated_at: "2014-01-24 16:02:52">], "2013-01-29"=>[#<Event id: 3, title: "Third", created_at: "2013-01-29 05:30:40", updated_at: "2014-01-29 05:30:40">], ...]}
P.S: I am using PostgreSQL
Now I get it by this way:
hash = {}
Meeting.where("extract(month from start_at) = ?", Date.today.month).pluck('DATE(start_at)').uniq.each do |date|
hash[date] = Event.includes(:meetings).where("DATE(meetings.start_at) = ?", date).references(:meetings)
end
But it produced so many queries to the database :(
Event.joins(:meetings).group('meetings.start_at') should do. But want you want is a group_by array method http://apidock.com/ruby/Enumerable/group_by so what you should do is
#events.group_by {|e| e.meeting.start_date}
In case of many to many you should be better off with
result = Hash.new
Meeting.include(:events).each {|m| result[m.start_at]||=[]; result[m.start_at] << m.events}
and with one liner you could
Meeting.includes(:events).inject(Hash.new) do |result, m|
result[m.start_at]||=[]
result[m.start_at] << w.events
result
end
This code should execute two database calls i think

Compare two arrays using include?

I have two arrays:
#all_genres = [#<Genre id: 1, name: "Action", created_at: "2013-03-01 07:44:51", updated_at: "2013-03-01 07:44:51">,
#<Genre id: 2, name: "Adventure", created_at: "2013-03-01 07:44:51", updated_at: "2013-03-01 07:44:51">,
#<Genre id: 3, name: "Animation", created_at: "2013-03-01 07:44:51", updated_at: "2013-03-01 07:44:51">]
#genres = ["Action", "Animation"]
I am trying to find the Genre.id from #genres compared to the #all_genres table. For example my result should be:
#genre_ids = [1, 3]
I have tried this:
#all_genres.each do |g|
if g.name.include?((#genres.each {|g| g}).to_s)
#genre_ids << g.id
end
end
I tried this in my console and it seemed to work but when I put it into my app it returns:
#genre_ids = []
A more rail-sy version:
#genre_ids = Genre.where(name: #genres).pluck(:id)
Or you could try this one-liner:
#genre_ids = #all_genres.select{|g| #genres.include? g.name }.map(&:id)
I'm assuming that you're populating your #genres array with a call to Genre.all.
You could simply do something like this:
Genre.where("name IN (?)", %w[name action]).collect { |x| x.id }
If you want to retrieve the ids for the Genres with those names.

Resources