Ruby, iterate objects array in the right order - ruby

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

Related

How to re-order an array of objects based on a list of ids

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.

Ruby array access position in array

#product
returns a single record. By relationship this product belongs to a slot
#slot = Slot.where(['id = ?', #product.slot_id]).first
what needs to be accessed is the position x in the array of all #slots = Slot.order('id asc').all so that I can identify or iterate over the following n slots as per ruby array Class:
arr[x, n]
I am not sure I understand the question there are many methods of accessing the index for an Array e.g.
alphabet = ('a'..'z').to_a
alphabet[0]
#=> "a"
alphabet.values_at(2,3,12)
#=> ["c","d","m"]
alphabet.index('r')
#=> 17
alphabet.fetch(15)
#=> "p"
There are many more such as #at, #find_index, even #rindex which will look for the last occurance. If you need to iterate index's you can use each_index or each_with_index. Since your question does not truely explain the scenario all I can do is explain how to deal with Array indices. For a more pertinent answer please update your question to show both data and expected results.
Here is what I can gather from your question
#product = Product.find(some_id)
#slot = #product.slot
#slots = Slot.where("id > ?", #slot.id) #return all slots after the #product.slot
If want to get the Slot for a given Product you can just do this:
slot = #product.slot
assuming you have your relationships well defined.

Is it possible to sort a list of objects depending on the individual object's response to a method?

I am wanting to display a gallery of products where I include products that are both for sale and not for sale. Only I want the products that are for sale to appear at the front of the list and the objects that are not for sale will appear at the end of the list.
An easy way for me to accomplish this is to make two lists, then merge them (one list of on_sale? objects and one list of not on_sale? objects):
available_products = []
sold_products = []
#products.each do |product|
if product.on_sale?
available_products << product
else
sold_products << product
end
end
. . . But do to the structure of my existing app, this would require an excessive amount of refactoring due to an oddity in my code (I lose my pagination, and I would rather not refactor). It would be easier if there were a way to sort the existing list of objects by my product model's method on_sale? which returns a boolean value.
Is it possible to more elegantly iterate through an existing list and sort it by this true or false value in rails? I only ask because there is so much I'm not aware of hidden within this framework / language (ruby) and I'd like to know if they work has been done before me.
Sure. Ideally we'd do something like this using sort_by!:
#products.sort_by! {|product| product.on_sale?}
or the snazzier
#products.sort_by!(&:on_sale?)
but sadly, <=> doesn't work for booleans (see Why doesn't sort or the spaceship (flying saucer) operator (<=>) work on booleans in Ruby?) and sort_by doesn't work for boolean values, so we need to use this trick (thanks rohit89!)
#products.sort_by! {|product| product.on_sale? ? 0 : 1}
If you want to get fancier, the sort method takes a block, and inside that block you can use whatever logic you like, including type conversion and multiple keys. Try something like this:
#products.sort! do |a,b|
a_value = a.on_sale? ? 0 : 1
b_value = b.on_sale? ? 0 : 1
a_value <=> b_value
end
or this:
#products.sort! do |a,b|
b.on_sale?.to_s <=> a.on_sale?.to_s
end
(putting b before a because you want "true" values to come before "false")
or if you have a secondary sort:
#products.sort! do |a,b|
if a.on_sale? != b.on_sale?
b.on_sale?.to_s <=> a.on_sale?.to_s
else
a.name <=> b.name
end
end
Note that sort returns a new collection, which is usually a cleaner, less error-prone solution, but sort! modifies the contents of the original collection, which you said was a requirement.
#products.sort_by {|product| product.on_sale? ? 0 : 1}
This is what I did when I had to sort based on booleans.
No need to sort:
products_grouped = #products.partition(&:on_sale?).flatten(1)
Ascending and descending can be done by inter changing "false" and "true"
Products.sort_by {|product| product.on_sale? == true ? "false" : "true" }

Find Value in Array of ActiveRecord Objects vs. Find Value from Repeated DB Calls

I would just like to return true, if my Array of Contact(s) (model) contains a Contact with id equal to some value. For example:
#contacts = Contact.all
#someval = "alskjdf"
find_val(#contacts, #someval)
def find_val(contacts, val)
#contact.each do |c|
if c.id == val
return true
end
end
return false
end
I have to do this repeatedly in my app (at most about 100 times for this particular actions), in order to exclude some data from an external API that has a list of contacts. Is this going to be too expensive?
I thought I might be able to do something faster, similar to ActiveRecord find on the array after it's been pulled down from the db, but can't figure that out. Would it be too expensive to call ActiveRecord like this?
Contacts.find_by_id(#someval)
The above line would have to be called hundreds of times... I figure iterating through the array would be less expensive. Thanks!
The best approach would be to index the contacts in a hash, using the contact id as the key after you retrieve them all from the db:
contacts = Contact.all.inject({}) {|hash, contact| hash[contact.id] = contact; hash }
Then you can easily get a contact with contact[id] in a performant way.
One way to reduce the amount of code you have to write to search the array is to open the array class and make a custom instance method:
class Array
def haz_value?(someval)
if self.first.respond_to? :id
self.select { |contact| contact.id == someval }.length > 0
else
false
end
end
end
Then, you can call #contacts.haz_value? #someval. In terms of efficiency, I haven't done a comparison, but both ways use Array's built-in iterators. It would probably be faster to create a stored procedure in your database and call it through ActiveRecord, here is how you can do that.

Array from a custom class in ruby

I have a custom class,
class Result
end
and i want to create an array of objects from it, but i cannot figure out how to do it? Because results = Array.new creates a new array, but i cannot find where to pass the class?
Presuming I understand the question correctly, the answer is: you don't. Ruby is dynamically typed, so the array is just an array and doesn't need to know that it's going to contain objects of class Result. You can put anything into the array.
Are you looking for something like this,
class Result
end
result = Array.new(5) { Result.new }
#=> [#<Result>, #<Result>, #<Result>, #<Result>, #<Result>]
Obviously you can pass any number you want.
results = Array.new creates an empty array (as would results = [], which is more succinct). To create an array containing result objects, either create an empty array and add elements to it, or use an array literal like [element1, element2, ...].
For example results = [Result.new, Result.new, Result.new] would create an array containing three Result objects.
You should just be able to create as many Result objects as you need and append them to the array. The array can hold objects of any type.
result1 = Result.new
result2 = Result.new
result3 = Result.new
results = Array.new
results << result1
results << result2
results << result3
Your results array now has 3 Result objects in it.

Resources