Active Record: Get two random objects? - ruby-on-rails

What's the simplest way to get an array with three objects (Card), one of which I already have? The other two should be randomly selected from the database.
My current approach looks like this:
[
#deck.cards[rand(#deck.cards.size)],
#deck.cards[rand(#deck.cards.size)],
#mycard
].sort_by {rand}
The problem I have right now is that sometimes #mycard shows up twice in the array. How can this be avoided?

something like this might work:
class Card < ActiveRecord::Base
belongs_to :deck
named_scope :random, lambda {
{ :offset => Kernel.rand(Card.count) }
}
named_scope :not_in, lambda { |a|
{ :conditions => [ 'id NOT IN (?)', a ] }
}
end
my_cards = []
#mycard = Card.last
my_cards << #mycard
2.times {
my_cards << #deck.cards.not_in(my_cards.collect(&:id)).random
}

Get a card from the deck. Check it's not the samy as #mycard.
Get another card from the deck. Check it's not the same as #mycard or the previous card.
Pretty straightforward, I'd have thought.

You have to remove each card from the deck as you draw from it, unless you are going to reshuffle after each draw, in which case I would just draw again until you get a unique one you haven't already drawn.

Related

How to calculate specific rating count hash in ruby on rails?

So, I have an after_save hook on review model which calls calculate_specific_rating function of product model. The function goes like this:
def calculate_specific_rating
ratings = reviews.reload.all.pluck(:rating)
specific_rating = Hash.new(0)
ratings.each { |rating| specific_rating[rating] += 1 }
self.specific_rating = specific_rating
save
end
Right now, it returns
specific_rating => {
"2"=> 3, "4"=> 1
}
I want it to return like:
specific_rating => {
"1"=> 0, "2"=>3, "3"=>0, "4"=>1, "5"=>0
}
Also, is it okay to initialize a new hash everytime a review is saved? I want some alternative. Thanks
You can create a range from 1 until the maximum value in ratings plus 1 and start iterating through it, yielding an array where the first element is the current one, and the second element is the total of times the current element is present in ratings. After everything the result is converted to a hash:
self.specific_rating = (1..ratings.max + 1).to_h { |e| [e.to_s, ratings.count(e)] }
save
You could also do something like this -
def calculate_specific_rating
ratings = [1,2,3,4,5]
existing_ratings = reviews.group_by(&:rating).map{|k,v| [k, v.count]}.to_h
Hash[(ratings - existing_ratings.keys).map {|x| [x, 0]}].merge(existing_ratings)
end
which gives
{3=>0, 4=>0, 5=>0, 2=>3, 1=>1}

How to select the count of joins as metadata for the main model using activerecord

I have two tables that are something like:
users
id
name
active
items
id
user_id
color
Using Rails, I want to select the active users along with the number of items that are red or blue.
Something like:
User.where(active: true).joins(:items).where(items: {color: ['red', 'blue']}).count(:items)
I want the result to be an array of Users where the Users have an annotated number of items.
So it could end up like users = activerecord query, users.first.name == 'John', users.first.items_count == 3
What would you do?
Considering the color filter, I'd just do the count in ruby.
class User
scope :active, -> { where(active: true) }
def items_of_color(colors)
items.select{ |i| i.color.in?(colors) }
end
end
in the controller
#users = User.active.preload(items)
and then in the view, count the red and blue
user.items_of_color(['red', 'blue']).size
But, if RED and BLUE are special and commonly referenced, you can do this...
class User
...
has_many :red_and_blue_items, where -> ({color: ["red", "blue"]}), class_name: "Item"
end
And then, preload like so
#users = User.active.preload(:red_and_blue_items)
In the view
#users.first.red_and_blue_items.size
I don't say it is the solution, but this following statement
Item
.joins(:user)
.group(:user_id)
.where(users: { active: true }, color: ['red', 'blue'])
.count
return a list of user_id with its associated item count:
{
user_id_1 => count_1,
user_id_2 => count_2,
user_id_3 => count_3,
...
}

Getting a model's children

If I have a model, ie. User, and each user has_many :animals, how do I get a retrieve the list of all of a particular set of User's animals?
I'd like to do something like this:
a = User.where(:last_name => 'Statham').animals
and have a be a list of the Animal objects. I assume I'm missing something trivial and simple.
Try this:
Animal.where(user_id: User.where(last_name: "Statham"))
Yep, simple is right. You're getting a lot of users there, so you need to iterate through them.
stathams = User.where(:last_name => 'Statham')
stathams.each { |statham| statham.animals }
And if you want to avoid duplicates:
animals = []
stathams = User.where(:last_name => 'Statham')
stathams.each { |statham| animals << statham.animals }
animals.uniq!

Any possible way to add parameters to ON clause on include left joins on rails?

I have a huge complex query like this:
#objects = Object.joins({ x: :y }).includes(
[:s, { x: { y: :z } }, { l: :m },:q, :w,
{ important_thing:
[:h, :v, :c,:l, :b, { :k [:u, :a] }]
}
]).where(conditions).order("x.foo, x.bar")
Then i want to show all Objects and only Important_things that were created at between two dates.
If i put this on there where clause i dont get all Objects, only Objects that has Important_things between informed dates.
A solution using raw sql was this:
select * from objects left join important_things on important_things.object_id = objets.id and important_things.created_at between 'x' and 'y'
Instead of:
select * from objects left join important_things on important_things.object_id = objets.id where important_things.created_at between 'x' and 'y'
I really need all those objects and i don't want to use a raw SQL, any workaround or a possibility to pass parameters to the ON clause on an association?
I do this,
class VendorsRatings < ActiveRecord::Base
def self.ratings(v_ids,sort = "DESC")
joins("RIGHT OUTER JOIN vendors_lists v
ON v.vendor_id = vendors_ratings.vendor_id").where(conditions)
end
end
I did a ugly workaround:
class Parent < ActiveRecord::Base
cattr_accessor :dt_begin, dt_end
has_many :children, conditions: Proc.new { { created_at: (##dt_begin..##dt_end) } }
end
class MetasController < ApplicationController
def index
Parent.dt_begin = Date.parse(param[:dt_begin])
Parent.dt_end = Date.parse(param[:dt_end])
#parents = Parent.includes(:children).where("children.age = ?", params[:age])
end
end
So this way i get all Parents even if i dont have Children created_at between those specified dates.
And the most important part of it i have all objects inside the ActiveRecord.
But be careful because i did messed with cattr_accessor so i have to set everytime before i search for Parents.

Params contain an array that wants to be a hash

I have an array (coming from a file_field, :multiple => true) in my params that I want to turn into a hash so I can build associated models for each element and process in my create action.
Currently receiving:
{"gallery"=>{"name"=>"A Gallery", "photos_attributes"=>{"0"=>{"image"=>[#<1st Image data removed for brevity>, #<2nd Image data removed for brevity>]}}}, "commit"=>"Save"}
I'd like to turn it into something like:
{"gallery"=>{"name"=>"A Gallery", "photos_attributes"=>{"0"=>{"image"=>#<1st Image data removed for brevity>}, "1"=>{"image"=>#<1st Image data removed for brevity>}}}, "commit"=>"Save"}
considered something like this but it's clearly wrong:
i = 0
params[:gallery][:photos_attributes]["0"][:image].reduce({}) do |result, element|
result[i++.to_s] = element
end
What's the "Rail's Way"?
You need to return the result hash at the end of each iteration.
i = 0
params[:gallery][:photos_attributes]["0"][:image].reduce({}) do |result, element|
result[(i += 1).to_s] = element
result
end
I've done something similar when receiving data from an iOS device. But, if I understand what you want and what your model(s) look like, to get nested attributes to work you don't want it to look like:
{ "photos_attributes" => { "0" => <image1>, "1" => <image2>, ... }
You want it to look like:
{ "photos_attributes" => [ <image1>, <image2>, ... ] }
And to do that all you need to do is:
params["gallery"]["photos_attributes"] = params["gallery"]["photos_attributes"]["0"]["image"]
Now, if I've misunderstood what you need, to get what you've asked for what you have might work (I don't use much reduce aka inject) or you could use tap:
i = 0
params["gallery"]["photos_attributes"] = {}.tap do |hash|
params["gallery"]["photos_attributes"]["0"]["image"].each do |image|
hash[i.to_s] = image
i = i + 1
end
end
Not a whole lot better IMO.

Resources