sort by values not present in the database - ruby-on-rails

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.

Related

How can i convert an array to an rails ActiveRecord::Relation without changing array order?

I have number of restaurants. On the fly i sorted out restaurants and stored to an array. Later I converted this array to rails object. When I did this array sorted order gone. How can I keep array order while converting to an rails object?
Edit:-
#restaurants = [res1,res2,res3,res4,res5]
sorted_array = [res1,res5,res2,res4,res3]
#places =Restaurant.where(:id=> sorted_array.map(:&id))
then i got below order of rails objects
#places = [res1,res2,res3,res4,res5]
I want to order like below when I convert an array to a rails object.
How can I get same order as an array ?
#places = [res1,res5,res2,res4,res3]
This seems to do what you want (res1 ... res5 should be Restaurant objects)
ids = [res1,res5,res2,res4,res3].map(&:id)
#places = Restaurant.where(id: ids).order("FIELD(id, #{ids.join(',')})").all
More info, Maintaining order in MySQL "IN" query
Is that sorted array an array of Restaurant records? Curious because you're
trying to call #id on the map block. Where are res1-res5 defined?
Another thing to note is your & syntax looks off, I think you need to say sorted_array.map(&:id), otherwise it's trying to call #&id on each restaurant record.
If it's a small array length like 5, in this case, you could map from the sorted array:
#places = sorted_array.map { |restaurant| Restaurant.find(restaurant.id) }

rails order array by value when not in model

I created an array in a rails runner(this is not a model and has no attributes) like:
name_1 = 5
name_2 = 14
name_3 = 26
name_4 = 3
...
#names = [name_1, name_2, name_3, name_4, name_5]
Each "name_x" has an integer stored as its value.
How can I order the array so that it orders the output [highest => lowest] by the current values, and also shows the "name" [key, value] ?.
Currently,
puts #names
shows only the values with no order. Tks for pointing me in the right direction, theres many posts relating arrays but most asume its a model with attributes where you can say to order by the attribute. how do you order when you have no specific attributes like in this scenario?
If you want names (keys) and values, use a Hash.
#names = {name_1: 5, name_2: 14, name_3: 26, name_4: 3}
A Hash is Enumerable, so you can sort it. However sorting results in an array, but its easy to make that a Hash again, and hashes in Ruby maintain the order of insertion.
#names.sort_by{|k,v| v}
[[:name_4, 3], [:name_1, 5], [:name_2, 14], [:name_3, 26]]
#names.sort_by{|k,v| v}.to_h
{:name_4=>3, :name_1=>5, :name_2=>14, :name_3=>26}
The natural sort order is ascending (smallest to largest), but you can just negate the sort_by value, or reverse the resulting array.
#names.sort_by{|k,v| -v}.to_h
{:name_3=>26, :name_2=>14, :name_1=>5, :name_4=>3}
#names.sort_by{|k,v| v}.reverse.to_h
{:name_3=>26, :name_2=>14, :name_1=>5, :name_4=>3}

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.

Dynamically adding to an existing key value pair in ruby

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.

Best way to order records by predetermined list

In order to keep things simple I have avoided using an enum for an attribute, and am instead storing string values.
I have a list of all possible values in a predetermined order in the array: MyTypeEnum.names
And I have an ActiveRecord::Relation list of records in my_recs = MyModel.order(:my_type)
What is the best way to order records in my_recs by their :my_type attribute value in the order specified by the array of values in MyTypeEnum.names ?
I could try this:
my_recs = MyModel.order(:my_type)
ordered_recs = []
MyTypeEnum.names.each do |my_type_ordered|
ordered_recs << my_recs.where(:my_type => my_type_ordered)
end
But am I killing performance by building an array instead of using the ActiveRecord::Relation? Is there a cleaner way? (Note: I may want to have flexibility in the ordering so I don't want to assume the order is hardcoded as above by the order of MyTypeEnum.names)
You are definitely taking a performance hit by doing a separate query for every item in MyTypeEnum. This requires only one query (grabbing all records at once).
ordered_recs = Hash[MyTypeEnum.names.map { |v| [v, []] }]
MyModel.order(:my_type).all.each do |rec|
ordered_recs[rec.my_type] << rec
end
ordered_recs = ordered_recs.values.flatten
If MyTypeEnum contains :a, :b, and :c, ordered_recs is initialized with a Hash of Arrays keyed by each of the above symbols
irb(main):002:0> Hash[MyTypeEnum.names.map { |v| [v, []] }]
=> {:a=>[], :b=>[], :c=>[]}
The records are appended to the proper Array based on it's key in the Hash, and then when all have bene properly grouped, the arrays are concatenated/flattened together into a single list of records.

Resources