Dynamically adding to an existing key value pair in ruby - ruby-on-rails

Ok, so 8 months into Ruby Hashes are still proving somewhat of an enigma.
I pull 10 records from the database, each with its own category field. Many of the records with share the same category, so I want them grouped by their categories in a hash.
I understand that the Key's are always unique, which is what makes a Hash a Hash. What I am struggling to do is add values to the existing key in the hash.
def self.categorise_events
hash = {}
self.limit(10).map do |event|
if hash.key?(event.event_type) #check if the key already exists
hash[event.event_type][event] #add the event record to that key
else
hash[event.event_type] = event #create the key if it doesn't exist and add the record
end
end
hash
end
This is more of a gist of what I am trying to achieve. I've had other compositions which have been closer but still not quite doing it.

You can add to an existing hash like
hash[key] = value
but in your case, your value is a collection of values, so it's an array
hash[key] = []
hash[key] << value
so you can add to an existing group with
unless hash.key?(event.event_type)
hash[event.event_type] = []
end
hash[event.event_type] << event
Now, this can be accomplished with the builtin method #group_by as seen on the documentations. But in your case, because it's using ActiveRecord, you can consider using #group to group records using SQL and greatly improving the performance of the grouping
self.limit(10).group(:event_type)
Check out the docs here.

Key is always uniq. So if you want to add values to a key, that value should be Array.
In this situation you can use group_by. Try
hash = self.limit(10).group_by{|e| e.event_type}
It returns a Hash whose keys are event_type and values are Array of records.

Related

Adding a hash to jsonb column in active record

I am returning a params hash in a controller.
params[:questions] = {"ad6d1d19-f95b-228c-8a19-49150ad15f23"=>"answer21", "90783719-9cd6-23de-f3fb-9bc80e7a72a0"=>"answer22"}
With multiple key value pairs.
I have model Model1. This has a json column called answers.
Every time a params[:questions] is returned. I'm trying a create a Model1 object as follows:
Model1.create(answers: {q_id: the_id, ans: the_answer})
How can I create multiple key value pair answers column? If I iterate through the params[:questions] and create an object, it will create a new object for every pair. So that's not a solution.
Would this work?
m = Model1.new
params[:questions].each do |id, answer|
m.answers << {q_id: id, ans: answer}
end
m.save!
or maybe
hash = {}
params[:questions].each do |id, answer|
hash << {q_id: id, ans: answer}
end
Model1.create(answers: hash
)

Rails 4 Using Ruby Enumerable to query arrays using arrays

I have a model that stores an array in one of the table columns called 'attributes'. So 3 separate records might look like this:
Record 1
MyModel.attributes = {Red, Furry, Stinky}
Record 2
MyModel.attributes = {Red}
Record 3
MyModel.attributes = nil
Record 4
MyModel.attributes = {Blue, Furry, Sweet}
I'd like to query this array for any of another array, including nil. The results should return any records that have any of the attributes in the query array and any records where the attributes column is nil.
query_array = [Blue, Furry]
The answer to this query should provide Record 1, Record 3 and Record 4 -- again, it's not looking for ALL the
currently, I can do this if I just query
MyModel.all.select {|m| m.attributes["Furry"] or m.attributes["Blue"] }
But I want to be able to create the array dynamically and not handcode the m.attributes["attribute"]. I can't quite figure out how to do this without requiring all of the array items, I just want ANY of the array items and records with no attributes.
You probably should not call the column attributes as this name is already used for the attributes hash accessors in rails models. For the examples below i renamed this to tags
A simple solution would be to check for nil (always include those records) and check if the intersection of tags has any tags in it:
model_1.tags # => ['red', 'furry', 'stinky']
model_2.tags # => ['red']
model_3.tags # => nil
model_4.tags # => ['blue', 'furry', 'stinky']
search_tags = ['red', 'blue']
MyModel.all.select do |model|
model.tags.nil? || (model.tags & search_tags).any?
end
You could also write it as a nested loop:
search_tags = ['red', 'blue']
MyModel.all.select do |model|
model.tags.nil? || model.tags.any? { |tag| search_tags.include?(tag) }
end
This is all done in memory, in ruby itself. If you have 100_000 or 1_000_000 records, then all of them are fetched from DB, instantiated and then filtered.
So depending on your exact requirements and what DB you are using you could find an easier/more performant solution. Some ideas:
Store the tags in a separate table
Store the tags as a comma separated string and use a 'like' query
Use postgres JSON datatype and the query features postgres provides
You can try this
query_array = [Furry, Blue]
query_string = query_array.map{|query| "m.attributes[#{query}]" }.join(" || ")
#=> "m.attributes[Furry] || m.attributes[Blue]"
MyModel.all.select {|m| eval(query_string) }
Now, all you have to do is add more items into the query_array.

sort by values not present in the database

I would like to sort objects, but i want this sorting to not be based on the direct value i have stored in the database.
In the database there are integer values, 1,2,3... but there is also a hash, that specifies, what those values mean.
{1 => "a", 2 => "za", 3 => "xa"}.
So if an instance has value 3, it should be sorted as "xa". Can I achieve this goal with order() method? It is important to not use arrays, but rather ActiveRecord Relations
For the making of a temp_table and populate it with the values of your hash, this should work.
sqlQuery1 = ActiveRecord::Base.connection.execute("CREATE TEMP TABLE IF NOT EXISTS hash_tmp(id integer, hash_values integer)")
sqlQuery2 = ActiveRecord::Base.connection.execute(TRUNCATE hash_tmp)
Your_hash.each do |id, value|
sqlQuery3 = ActiveRecord::Base.connection.execute("INSERT INTO hash_tmp(id, hash_values) VALUES (#{id}, '#{value}')")
end
You should have a model for that temp table you created, then you can get all the values (filtered or not) and order by the new field:
#YourTemp = Yourtemp_model.all(:order=> "hash_values")
This is assuming that to this point your hash holds all the possible values which means you can iterate it and populate your temp table with them.

Updating current value in ActiveRecord (Rails)

in my application lots of objects are already preloaded by Rails.
Now I like to update some of these object attributes. Unfortunately some of these objects are related to the same object in my database. When I add a value to myObject.balance (myObject.balance += value), the attribute balance in differentButSameObject has still the same value.
One solution could be reloading the object. I would prefer to update the value like this:
UPDATE myTable SET balance = balance + 10 WHERE id = 1.
Is this possible?
You could use ActiveRecords update_all statement:
Object.where(:id => 1).update_all("balance = balance + 1")
You can add the following code to your model. It uses the update_counters which performs a proper query (that does COALESCE, for instance):
def inc(counter, by = 1)
raise "Cannot update column of a non-persisted model" unless persisted?
self.class.update_counters(id, counter => by)
increment(counter, by)
end

Postgres ORDER BY values in IN list using Rails Active Record

I receive a list of UserIds(about 1000 at a time) sorted by 'Income'. I have User records in "my system's database" but the 'Income' column is not there. I want to retrieve the Users from "my system's database"
in the Sorted Order as received in the list. I tried doing the following using Active Record expecting that the records would be retrieved in the same order as in the Sorted List but it does not work.
//PSEUDO CODE
User.all(:conditions => {:id => [SORTED LIST]})
I found an answer to a similar question at the link below, but am not sure how to implement the suggested solution using Active Record.
ORDER BY the IN value list
Is there any other way to do it?
Please guide.
Shardul.
Your linked to answer provides exactly what you need, you just need to code it in Ruby in a flexible manner.
Something like this:
class User
def self.find_as_sorted(ids)
values = []
ids.each_with_index do |id, index|
values << "(#{id}, #{index + 1})"
end
relation = self.joins("JOIN (VALUES #{values.join(",")}) as x (id, ordering) ON #{table_name}.id = x.id")
relation = relation.order('x.ordering')
relation
end
end
In fact you could easily put that in a module and mixin it into any ActiveRecord classes that need it, since it uses table_name and self its not implemented with any specific class names.
MySQL users can do this via the FIELD function but Postgres lacks it. However this questions has work arounds: Simulating MySQL's ORDER BY FIELD() in Postgresql

Resources