I have a typical has_many relation between two models, let's say Order and Item.
Also, I am using nested attributes like so:
class Order < ActiveRecord::Base
has_many :items
accepts_nested_attributes_for :items
end
When putting together a nested edit order form, I want to be able to build new items (where an item has a name and a quantity) and insert them at arbitrary positions within the previously saved list of items.
Suppose I have a sorted array of strings listing all_possible_item_names a customer can specify a quantity for.
Until rails 3.2.13, #order.items was a simple array, and I could use ruby's own Array#insert method to insert new items wherever I wanted:
# after loading the original order, this code will build additional items
# and insert them in the nested edit order form with a default qty of 0
all_possible_item_names.each_with_index do |name, pos|
unless #order.items.find { |i| i.name == name }
#order.items.insert pos, Item.new(name: name, qty: 0)
end
end
In rails 4, on the other hand, #order.items is a ActiveRecord::Associations::CollectionProxy, and insert has a different meaning.
How can I accomplish in rails 4 what I used to be able to do in rails 3?
Convert it into array then insert
#order.items.to_a.insert pos, Item.new(name: name, qty: 0)
Related
I have 2 models as below:
class Student < ActiveRecord::Base
has_many :marks
end
class Mark < ActiveRecord::Base
belongs_to :student
end
point is a field in marks table. For each student there are many entries in the marks table.
I need to get a list of students based on the order of highest total points.
I tried as below:
#students = Student.all.collect{|p| [p,p.marks.pluck(:point).sum]}
#students.sort_by { |h| h[1] }.reverse!
But it will return 2 items in each array, one the object and next the total points.
Is there a better solution please.
Thanks,
Jissy
This should do the trick:
Student.joins(:marks).select('students.*, SUM(marks.point) AS total_point').group(:id).order('total_point DESC')
You can do it like this.
Student.joins(:marks).select('id, sum(marks.point) as total_marks').group('students.id').order('total_marks desc')
If you are still unable to run it please modify it or let me know.In place of id in select you can select any column(s).
I have an object Task as you can see below:
class Task
include HTTParty
attr_accessor :id, :name, :assignee_status, :order
def initialize(id, name, assignee_status)
self.id = id
self.name = name
self.status = status
self.order = order
end
end
So, when I load a list of tasks, I give them a specific order, like this:
i = 0
#tasks.each do |at|
at.order = i
i += 100
end
This list is, then, sent over json to a client app which shows is to the user and allows for drag'n'drop. After some task is re-ordered, the front-end app sends a list back to the server with all the task ids in the new order.
Example:
23,45,74,22,11,98,23
What I want to do is the re-order the array of objects based on the ids that I found. What I thought about doing was to set all order to 0 and then search each object individually on the matrix and set their priority accordingly. But this just feels wrong... feels to computing intensive.
Is there a clever way to do something like:
ArrayOfTaskObjects.orderbyAttribute(id) ??
Maybe the easy way is to iterate the params with a index, something like:
params['task_ids'].split(",").each_with_index do |task_id, idx|
task = lookup_the_task_based_on_id
task.order = idx
end
def sort_by_id(array)
r = array.inject({}) { |hash, element| hash.merge!(element.id => element) }
temp_array = r.sort_by {|k,_| k}
temp_array.flatten!.grep(temp_array.last.class)
end
What's happening:
The sort_by_id method takes in an array of objects - this being your Task array.
In the first line of code a hash is created which stores each Task id as the key and the Task object as the value.
The second line of code sorts the hash based on the keys (note that the r.sort_by method returns a two dimensional array, ex. [[23, Task], [44, Task], [54, Task]]).
And finally the third line of code flattens your two dimensional array into a one dimensional array and greps removes all id's from the two dimensional array leaving the array of Tasks in order.
I'm looking to define a method on one of my objects that will return a just one column of data from all of its child objects so long as another column in the same record meets certain conditions.
For instance if I have two objects
ParentObject
has_many: child_objects
#fields
name (string)
ChildObject
belongs_to: parent_object
#fields
name (string)
whitelisted_at (datetime)
I've read up that I can get a list of all child_object records for a parent_object based on a conditional specified using .where(). For instance in my controller I have code like so:
ParentObject.child_objects.where("whitelisted_at IS NOT NULL")
This gives me an active record associate like so:
#<ActiveRecord::AssociationRelation [
<ChildObject id: 1, name:"Susan", whitelisted_at: "2015-02-18 12:07:37">,
<ChildObject id: 1, name:"Simon", whitelisted_at: "2015-02-18 12:07:37">,
<ChildObject id: 1, name:"Sally", whitelisted_at: "2015-02-18 12:07:37">
]
I was looking how I would then filter through these to return an array of just names. Ideally i'd be able to run this all as a Model method so:
class ChainObject < ActiveRecord::Base
...
def whitelisted_names
#... outputs [Susan, Simon, Sally]
end
end
What would be the most concise and rails-y way of doing this. I thought about doing a .where() then an .each() and having a block method but that seems really cumbersome and I'm sure I'm just missing some smart ActiveRecord or Association method that could pluck an array of values from multiple hashes. I'm pouring over the APIdock but I think the problem is I don't know how to describe what I'm trying to do!
In your parent model you could use where.not and use the pluck method ActiveRecord gives you (props to Stefan - see pluck)
class ParentObject < ActiveRecord::Base
...
def whitelisted_names
child_objects.where.not(whitelisted_at: nil).pluck(:name)
end
end
I have an array of objects and every object respond to the 'order' method.
I can write the method
objects = Objects.all
objects.each do |i|
puts i.order
end
but I'm not sure the order is correct. Is there any fast way to iterate my array following the right order of every object?
Update: real case.
class Articles < ActiveRecord::Base
as_many :article_pages
end
a = Article.find(2345)
pages = a.article_pages
pages.each ...
pages.first.order = 1
pages.last.order = 5
I need to iterate in my pages following order...
a.article_pages.order('order').each
doesn't work
By default, ActiveRecord will not guarantee any order. To change that, use:
a = Article.find(2345)
pages = a.article_pages.order('order asc')
to order by a column. Switch asc to desc to order in descending order.
You can use sort option in case you want it in ascending only
a = Article.find(2345)
pages = a.article_pages.all.sort_by &:order
I'd like to select a collection of items ordered based on the number of items within an array. Hopefully the following example will clarify my rather poor explanation:
class Thing
include MongoMapper::Document
key :name, String
key :tags, Array
end
I'd like to retrieve all Things ordered from those with the most tags to those with the least. The tags in this example are simply strings within the tags array. Basically I want something which means the same as this (but works):
Thing.all(:order => 'tags.count desc')
Is this possible?
The core server currently doesn't support computing the size of an array and then sorting by that. I think that your best bet for the moment would be to cache the array size on your own, and add an index on that field.
class Thing
include MongoMapper::Document
key :name, String
key :tags, Array
key :tag_size, Integer, :default => 0, :index => true
end
Then just add a callback to your model that updates tag_size on save.
If this is a feature you'd like to see in the core server, you can add a case here:
http://jira.mongodb.org/browse/SERVER