Removing "duplicate objects" - ruby-on-rails

Let's say I have an array of objects from the same class, with two attributes of concern here: name and created_at.
How do I find objects with the same name (considered dups) in the array, and then delete the duplicate record in the database. The object with the most-recent created_at date, however, is the one that must be deleted.

seen = []
#sort by created date and iterate
collection.sort({|a,b| a.created_at <=> b.created_at}).each do |obj|
if seen.map(&:name).include? obj.name #check if the name has been seen already
obj.destroy!
else
seen << obj #if not, add it to the seen array
end
end
Should do the job hopefully.

If this is just a one-time bugfix before introducing UNIQUE INDEX on the table, you might as well do it in SQL:
DELETE FROM t WHERE id IN (
SELECT t1.id
FROM t t1
LEFT JOIN t t2 ON t1.name = t2.name AND t2.created_at < t1.created_at
WHERE t2.id IS NOT NULL
)

Related

How do I group axlsx file to sort alphabetically by org

Org is a column in attendee that's nested in this includes statement. I want it so that it groups attendee.org alphabetically
#meetings = NwMeeting.includes(meeting_attendees: [:attendee]).where(show_id: #show.id)
#meetings.each do |meeting|
#nw_attendee_meetings = meeting&.meeting_attendees(&:attendee)
#nw_attendee_meetings.each do |nw_attendee_meeting|
attendee = nw_attendee_meeting&.attendee
data = []
data << attendee&.org
data << attendee&.name
data << meeting&.status&.capitalize
AxlsxTools.add_row(data) (edited)
Because you want to list Attendee records, it may be a little bit easier if you switch the "main" model you query to Attendee. Joining the NwMeeting and SELECT the status column, then you can just do ORDER BY attendees.org ASC, the records with NULL value will be placed in front of others by default.
#attendees = Attendee.joins(meeting_attendees: :nw_meeting)
.select("attendees.*, nw_meetings.status AS meeting_status")
.where(nw_meetings: { show_id: #show.id })
.order("attendees.org ASC")
# .order("attendees.org ASC NULL FIRST") if you use PosgreSQL
#attendees.each do |attendee|
data = []
data << attendee.org
data << attendee.name
data << attendee.meeting_status&.capitalize
AxlsxTools.add_row(data)
end
btw, #engineersmnky 's Arel method is more advanced, and that should work, too :)

Move specific item to last position

Let there is a model Item with attributes id, name and specific record in DB with name 'other'.
How to get in single SQL query an ActiveRecord::Relation object with items sorted in way that other item is at last position?
As temporarily solution I used Item.where.not(name: 'other') + Item.where(name: 'other') but it results in 2 queries.
NB: it's not a real code but an extremely simplified example.
Perhaps something like below will help - we read all Items, and use slice to remove the Item with name equal to other, and append it back to array at the end.
a = Item.all.tap do |arr|
arr << arr.slice!(arr.index { |t| t.name == "other"})
end
if i get you right then you're looking for a SQL query, right? Then you could try something like that:
select id, name
from (
select id, name, decode(name, 'other', 999, 1) as sort
from items
)
order by sort
I have no database right now to test the statement. But I think you get the idea.
Good luck
Resolved with adding class method to Item model:
def self.sorted
find_by_sql("SELECT * FROM items ORDER BY (CASE name WHEN 'other' THEN 0 ELSE 1 END) DESC, id ASC")
end

ActiveRecord - Getting the sum of user score

I have an "ITEMS" database made of; ITEM_ID, OWNER_ID and VALUE.
An owner can own unlimited items. An item can only have one owner.
And I have an "OWNER" database made of; ID, NAME
I want to find the NAMES of top 10 RICHEST (wealthier) people. How can I do that?
First, I need to sum the values of an owner_id; than compare that with others?
Here is what you can do :
Item.group(:owner_id) # grouping Items by owner id
.select("SUM(value) as sum") # summing values of each group
.order("sum DESC") # ordering resulting records by the sum value
.limit(10) # giving the top 10 records
It is kinda long solution but it worked for me:
toplist = []
all_owners = Owner.all
all_owners.each do |owner|
name = Owner.find(owner).name
owner_value = Item.where(owner_id: owner).sum(:value)
toplist << [owner_value,name]
end
#top10 = toplist.sort.last(10).reverse

Produce a report by getting data from multiple tables

I have 4 tables:
key: id, name
project: id, name
project_key: id, key_id, project_id
project_report: id, status, project_id, key_id
project_c_report: id, status, project_id, key_id, c_id
I want to produce a report using those tables:
The output should be:
Key.name, project_report.status, project_c_report.status
I was able to do this by getting all the keys from a project, and loop over them
array = []
project.keys.each do |k|
p = ProjectReport.where(keyword_id: k, project_id: p.id).map(&:status)
c = ProjectCReport.where(keyword_id: k, project_id: p.id, c_id:1).map(&:status)
array << {name: k.name, pr: p, pcr: c}
end
array
The problem is that I am doing a lot of selects and everything is slow, can someone help me please with a better way of doing this.
Thank you
First, create a function in your DataBase. This is just a brief example, and also its done in PostgreSQL but shouldnt difer much
from MySQL, SQLServer, etc
Function get_myreport(key_id integer, project_id integer [As many params as you'd like the function to get))
pProject ALIAS FOR $1;
pKey ALIAS FOR $2;
BEGIN
CREATE TEMP TABLE IF NOT EXISTS tmp_project_report(id integer, project_name character varying, *All the values you want to see in the report);
TRUNCATE tmp_project_report;
INSERT INTO tmp_project_report(all the columns)
SELECT a.table1_fields, b.table2_fields, c.table3_fields, d.table4_fields, e.table5_fields
FROM table1 a, table2 b, table3 c, table4 d, table5 e
WHERE
a.key = pKey
AND b.project_key = pProject
END;
Then, in your controller's method you call the up the function like this
myFunction = ActiveRecord:Base.connection.execute = "Select get_myreport("param1, param2, etc...")
You will have to make a model where you put all the fields that are on the temp_table you've made, and also you will set the temp_table as the self.table_name
Then, in your view, you'd only have to iterate on your collection and display the values accordingly
#report = TempTable.all
<% #report.each_do |report| %>
<% report.value1 %>
<% etc... %>
<% end %>
Figure out the database query, then query the database directly from your model:
def records
connection = ActiveRecord::Base.connection
records = connection.select %Q {
SELECT key.name, project_report.status, project_c_report.status
FROM ...
JOIN ...
;
}
records
end
Here is something you can try if you choose to keep this within Rails (note that the following query is untested and is shown for concept only):
report_data = Project.joins(project_key: :key)
.joins('left join project_reports on project_keys.project_id = project_reports.project_id and project_keys.key_id = project_reports.key_id
left join project_c_reports on project_keys.project_id = project_c_reports.project_id and project_keys.key_id = project_c_reports.key_id')
.where('project_c_reports.c_id = ?', 1)
.select('projects.name, project_reports.status as report_status, project_c_reports.status as c_report_status')
This should give you an array of Project objects each including the selected three attributes name, report_status, c_report_status. To get these values in an array of these three elements you could do:
report_data.map { |p| [ p.name, p.report_status, p.c_report_status ] }
The type of join for the query depends on your requirement. Given the index are in place the query should be better compared to how it looks in code!

Ruby/Rails - Find Foreign Key With Most Instances in Model

I have a joined model that has a foreign key for the model event.
The joined model is called Goals. I'm trying to find the proper find condition to figure out which event_id has the most instances in the Goal join model. Essenially which foreign key id has the most entries in the join model.
Is there a way to do this?
Goal.where(:event.id => ??????? ).first
Couldn't come up with a more elegant solution but try this:
results = Goal.connection.select_all('SELECT COUNT(*) as amount, event_id FROM goals GROUP BY event_id ORDER BY amount DESC LIMIT 0, xx')
raise results.inspect
If you just want the one most event_id with most entries you can also use:
event_id = Goal.connection.select_one('SELECT COUNT(*) as amount, event_id FROM goals GROUP BY event_id ORDER BY amount DESC LIMIT 1').first
If you have set up your models correctly you should be able to do this (if this is not the case then: setup your models correctly):
if Event.all.length == 0
return
end
eventMax = Event.first
Event.all.each do |e|
eventMax = e.goals.length>eventMax.goals.length?e:eventMax
end
#output or do whatever with your newly found event
puts eventMax.to_json
The solution from Danny is not really good.
You should never (or at least very rarely) have to write sql by yourself in rails.

Resources