How to "merge" "duplicate" active record object? - ruby-on-rails

So, I'm building a map where i put pins (Google map).
I have a query that gives me position of my pins, however I can have multiple pins with the exact same position but a different description.
I've been banging my head against the desk on this since this morning for some reason I can't get a working solution,
Here is what I have so far:
building_permits = BuildingPermit.select('latitude, longitude, street, city, state, permit_number, description, type, id').where(:contractor_id => params[:nid])
#bp = Array.new
building_permits.each do |bp|
#bp.push({"lat" => bp.latitude, "lng" => bp.longitude, "desc" => "<p><b>#{bp.street} #{bp.city}, #{bp.state}</b></p><p><b>Description:</b>#{bp.description}</p><p><b>Permit #{bp.permit_number}</b></p><p><b>#{bp.type}</b></p>", "id" => bp.id})
end
nb_rm = 0
building_permits.each_with_index do |bp, index|
index1 = index
building_permits.each_with_index do |bp2, index|
if bp.longitude == bp2.longitude && bp.latitude == bp2.latitude && bp.id != bp2.id
#debugger
if #bp[index1].present?
#bp[index1]["desc"] << "<br /><br /><b>Description:</b>#{bp2.description}</p><p><b>Permit #{bp2.permit_number}</b></p><p><b>#{bp2.type}</b></p>"
#bp.delete_at(index-nb_rm)
nb_rm += 1
end
end
end
end
I'm sure there is something really stupid that's screwing the whole thing, but can't find it.

Try to duplicate your pin location:
# rails < 3.1
new_record = old_record.clone
#rails >= 3.1
new_record = old_record.dup
#and then
new_record.save
And after all change description of the pin.

I had a similar problem and wanted to post my solution -- I had duplicate authors and wanted to find any authors where the name attribute was equal, and move related objects (books and recommendations) onto the first of each duplicate group, then delete the others.
Author.all.each do |a|
#name = a.name
if Author.where(:name => #name).count > 1
first = Author.where(:name => #name).first
a.books.each{|b| b.update_attributes(author_id: first.id)}
a.recommendations.each{|r| r.update_attributes(author_id: first.id)}
end
end
Author.all.each do |a|
if a.books.count == 0 && a.recommendations.count == 0
a.delete
end
end
Hopefully that can be helpful to someone!

Related

Rails Remove Model from ActiveRecord::Relation Query

What's the best way to dynamically remove a model from a query? Basically I want to find all campaigns where a user hasn't already provided a response.
The below method delete_at actually deletes the model which isn't what I want. I only want it remove from the local 'campaigns' ActiveRecord::Relation query set that I got.
def self.appuser_campaigns appuser_id, language
appuser = Appuser.find(appuser_id)
campaigns = Campaign.check_language language
i = -1
campaigns.each do |campaign|
i = i + 1
responses = Response.where(appuser_id: appuser_id, campaign_id: campaign.id)
if responses.length > 0
campaigns.delete_at(i)
end
end
puts campaigns.class.name #"ActiveRecord::Relation"
campaigns
end
def self.check_language language
campaigns = Campaign.where(language: language, status: "In Progress")
end
You can do the following:
already_answered_campaign_ids = Appuser.find(appuser_id).responses.pluck(:campaign_id)
Campaign.where('id NOT IN (?)', already_answered_campaign_ids.presence || -1)

Ruby: DRY out creation of Active Record Relation object

I am building up an Active Record Relation object in steps, allowing a user to filter a list of car parts by make and model of car:
parts = Part.where(:listed => (start_date..end_date))
unless filters[:make_id].nil? || filters[:make_id] == 0
parts = parts.joins(:car).
where("cars.make_id = ?", filters[:make_id] )
end
unless filters[:model_id].nil? || filters[:model_id] == 0
parts = parts.joins(:car).
where("cars.model_id = ?", filters[:model_id] )
end
etc...
This is repetitive, and I would like to pull this out into a method that takes 'make_id' and 'model_id' as parameters. One important feature to retain is that if a filter is nil (e.g. the user doesn't specify the model) then parts for all models are returned.
I can't figure out how to tackle this and I'm not sure what to Google. Can you help?
parts = Part.where(:listed => (start_date..end_date))
[:make_id, :model_id].each do |k|
filters[k]
.tap{|f| parts = parts.joins(:car).where("cars.#{k} = ?", f) unless f.to_i.zero?}
end

Ruby on Rails: Search on has_many association with inner join and other conditions

I've been searching for this for a long time now, and I can't get to find the answer anywhere.
What I have is a basic search function to find houses, everything works, and till so far the main piece for getting houses looks something like this (the conditions are for example):
#houses = House.all(
:conditions => ["houses.province_id = ? AND open_houses.date > ? AND houses.surface = ?", "4", "Tue, 08 Feb 2011", "125"],
:select => 'distinct houses.*',
:joins => "INNER JOIN open_houses ON open_houses.house_id = houses.id" )
Now, I have this has_many association for the specifications of a house (like balcony, swimming pool etc)..
For this I have the standard setup. A table for the spec-names, and a table with the house_id and the specification_id.
But now, I need to add these to the search function. So someone can find a house with Swimming pool AND a balcony.
I'm sure there is a solution, but I just don't know where to put it in the code, and how.. Google just get's me to pages like it, but not to pages explaining exactly this.
This is what my params look like:
Parameters: {"dateto"=>"", "commit"=>"ZOEKEN!", "pricefrom"=>"", "location"=>"", "province_id"=>"4", "rooms"=>"", "datefrom"=>"", "surface"=>"125", "utf8"=>"✓", "priceto"=>"", "filters"=>{"by_specifications"=>["2", "5", "10"]}, "house_category_id"=>""}
Hope someone can help, if something is unclear please let me know.
Thanks
EDIT:
Oke, I've got it to work! Thanks very much!
There's only one little problem: A house shows up if either one of the specs exists for the house.. So it's an OR-OR query, but what I want is AND-AND..
So if a user searches for balcony and garage, the house must only show up if both of these exist for the house..
What I did now is this: for each specification searched, a query is being made.. (code is below)
I'm wondering, is this the right way to go?
Because it works, but I get double matches.. ( I filter them using "uniq" )
The problem with uniq is that I can't get "will_paginate" to work with it..
This is my final code:
def index
ActiveRecord::Base.include_root_in_json = false
# I'm creating conditions with a function I made, PM me for the code..
conditions = createConditions( createParameters( ) );
query = House.includes( :open_houses ).where( conditions )
unless params[:specifications].blank?
query = query.joins( :house_specifications )
query = query.group( 'open_houses.id' )
query = query.where( 'house_specifications.specification_id' => params[:specifications] )
query = query.order( 'count(open_houses.id) DESC' )
# query = query.having( 'count(open_houses.id) = ?', [params[:specifications].length.to_s] )
end
query = query.order( (params[:sort].blank?)? "open_houses.date ASC, open_houses.from ASC" : params[:sort] )
if params[:view] == "list"
page = params[:page] unless params[:page].blank?
#houses = query.all.uniq.paginate( :page => page || "1", :per_page => 5 )
else
#houses = query.all.uniq
end
respond_to do |format|
format.html
format.js
end
end
Thanks for the help, really appreciate it!
Try this (Rails 3):
House.joins(:houses_specifications).
where('houses_specifications.specification_id' => params[:by_specifications]).
where(...).all
You can have a bunch of if's and case's to add filters, group_by's, limits etc before you run that final .all on the query.
house_query = House.where(:pink => true)
unless params[:by_specifications].blank?
house_query = house_query.joins(:houses_specifications).
where('houses_specifications.specification_id' => params[:by_specifications])
end
...
#houses = house_query.all
Edit:
New and improved version that doesn't query directly on the join table for readability, group_by for distinctness and does an intersect to get houses with all specs.
query = House.joins(:open_houses).where(conditions)
unless params[:specifications].blank?
query = query.joins(:specifications).
group('houses.id').
where(:specifications => params[:specifications]).
having('count(*)=?', params[:specifications].count)
end
if params[:view] == "list"
#houses = query.all.paginate(:page => params[:page])
else
#houses = query.all
end
You can change the "having" to an "order" to get those with the most matching specs first.

How to store the result of my algorithm?

I have an algorithm that searches through all of my sites users, finding those which share a common property with the user using the algorithm (by going to a certain page). It can find multiple users, each can have multiple shared properties. The algorithm works fine, in terms of finding the matches, but I'm having trouble working out how to store the data so that later I'll be able to use each unit of information. I need to be able to access both the found users, and each of the respective shared properties, so I can't just build a string. This is an example of the output, being run from the perspective of user 1:
user 4
sharedproperty3
sharedproperty6
user 6
sharedproperty6
sharedproperty10
shareproperty11
What do I need to do to be able to store this data, and have access to any bit of it for further manipulation? I was thinking of a hash of a hash, but I can't really wrap my head around it. I'm pretty new to programming, and Ruby in particular. Thanks for reading!
EDIT - Here's the code. I'm fully expecting this to be the most incorrect way to do this, but it's my first try so be gentle :)
So if I'm understanding you guys correctly, instead of adding the interests to a string, I should be creating an array or a hash, adding each interest as I find it, then storing each of these in an array or hash? Thanks so much for the help.
def getMatchedUsers
matched_user_html = nil
combined_properties = nil
online_user_list = User.logged_in.all
shared_interest = false
online_user_list.each do |n| # for every online user
combined_properties = nil
if n.email != current_user.email # that is not the current user
current_user.properties.each do |o| # go through all of the current users properties
n.properties.each do |p| # go through the online users properties
if p.interestname.eql?(o.interestname) # if the online users property matches the current user
shared_interest = true
if combined_properties == nil
combined_properties = o.interestname
else
combined_properties = combined_properties + ", " + o.interestname
end
end
end
if shared_interest == true
matched_user_html = n.actualname + ": " + combined_properties
end
end
end
end
return matched_user_html
render :nothing => true
end
This returns an array of hashes with all users and their corresponding sharedproperties.
class User
def find_matching_users
returning Array.new do |matching_users|
self.logged_in.each do |other_user|
next if current_user == other_user # jump if current_user
# see http://ruby-doc.org/core/classes/Array.html#M002212 for more details on the & opreator
unless (common_properties = current_user.properties & other_user.properties).empty?
matching_users << { :user => other_user, :common_properties => common_properties }
end
end
end
end
end
In your view you can do something like this:
<%- current_user.find_matching_users.each do |matching_user| -%>
<%-# you can acccess the user with matching_user[:user] -%>
<%-# you can acccess the common properties with matching_user[:common_properties] -%>
<%- end -%>
You can use a hash table with the key being the user object and the value being an array of the shared properties . This is assuming that you first need to do a lookup based on the user .
Something like this :
#user_results = { user1 => [sharedproperty3,sharedproperty7] , user2 => [sharedproperty10,sharedproperty11,sharedproperty12]}
You can then acces the values like :
#user_results[user1]
or you can also iterate over all the keys using #user_results.keys

Removing “duplicate objects” with same attributes using Array.map

As you can see in the current code below, I am finding the duplicate based on the attribute recordable_id. What I need to do is find the duplicate based on four matching attributes: user_id, recordable_type, hero_type, recordable_id. How must I modify the code?
heroes = User.heroes
for hero in heroes
hero_statuses = hero.hero_statuses
seen = []
hero_statuses.sort! {|a,b| a.created_at <=> b.created_at } # sort by created_at
hero_statuses.each do |hero_status|
if seen.map(&:recordable_id).include? hero_status.recordable_id # check if the id has been seen already
hero_status.revoke
else
seen << hero_status # if not, add it to the seen array
end
end
end
Try this:
HeroStatus.all(:group => "user_id, recordable_type, hero_type, recordable_id",
:having => "count(*) > 1").each do |status|
status.revoke
end
Edit 2
To revoke the all the latest duplicate entries do the following:
HeroStatus.all(:joins => "(
SELECT user_id, recordable_type, hero_type,
recordable_id, MIN(created_at) AS created_at
FROM hero_statuses
GROUP BY user_id, recordable_type, hero_type, recordable_id
HAVING COUNT(*) > 1
) AS A ON A.user_id = hero_statuses.user_id AND
A.recordable_type = hero_statuses.recordable_type AND
A.hero_type = hero_statuses.hero_type AND
A.recordable_id = hero_statuses.recordable_id AND
A.created_at < hero_statuses.created_
").each do |status|
status.revoke
end
Using straight Ruby (not the SQL server):
heroes = User.heroes
for hero in heroes
hero_statuses = hero.hero_statuses
seen = {}
hero_statuses.sort_by!(&:created_at)
hero_statuses.each do |status|
key = [status.user_id, status.recordable_type, status.hero_type, status.recordable_id]
if seen.has_key?(key)
status.revoke
else
seen[key] = status # if not, add it to the seen array
end
end
remaining = seen.values
end
For lookups, always use Hash (or Set, but here I thought it would be nice to keep the statuses that have been kept)
Note: I used sort_by!, but that's new to 1.9.2, so use sort_by (or require "backports")

Resources