how to make an array into array of arrays in ruby? - ruby-on-rails

I have a query company_in_outs = company.in_outs.where('date >= ? and date <= ?', Date.today.at_beginning_of_month, Date.today.end_of_month) which is returning me <ActiveRecord::AssociationRelation. now i want to make this array into an array of arrays, those are grouped based on the date value.
for example.
#<ActiveRecord::AssociationRelation [#<InOut id: 2806, date: "2016-06-01", created_at: "2016-06-02 07:01:52", updated_at: "2016-06-16 07:43:45", company_id: 1>,#<InOut id: 2806, date: "2016-06-01", created_at: "2016-06-02 07:01:52", updated_at: "2016-06-16 07:43:45", company_id: 1>,#<InOut id: 2806, date: "2016-06-01", created_at: "2016-06-02 07:01:52", updated_at: "2016-06-16 07:43:45", company_id: 1>,#<InOut id: 2806, date: "2016-06-02", created_at: "2016-06-02 07:01:52", updated_at: "2016-06-16 07:43:45", company_id: 1>,#<InOut id: 2806, date: "2016-06-02", created_at: "2016-06-02 07:01:52", updated_at: "2016-06-16 07:43:45", company_id: 1>,#<InOut id: 2806, check_in: "2016-06-24 16:16:00", check_out: "2016-06-25 01:16:00", date: "2016-06-01", created_at: "2016-06-02 07:01:52", updated_at: "2016-06-16 07:43:45", company_id: 1> ]
from this array i want to make an array of arrays, which needs to be grouped based on the date. meaning all records which contains same date needs to be grouped as one array. like this i want to have an array of arrays.

If you already have all the records loaded:
company_in_outs.each_with_object({}) do |record, hash|
(hash[record.date] ||= []) << record
end.values
if you aren't using Rails (ActiveSupport) you should use inject instead of each_with_object
company_in_outs.inject({}) do |hash, record|
(hash[record.date] ||= []) << record
hash
end.values
I think you should be able to done sorting on db site too.

There is actually a group_by method in Array, but instead of returning array of array, it returns an object with key and array of the same key as value.
In your case:
company_in_outs.group_by do |com|
com.date
end
The code above will return an object grouped by date as a key, and array of the same date as value.
You could loop through the returning object which will return key and value, and do whatever you like :)
Hope this helps!

Related

Spree: Return only Orders with Line Items in them

I'm having trouble setting up the controller in a spree app so it only returns orders with line_items in them.
<Spree::Order id: 1057, number: "R498797188", item_total: #<BigDecimal:7f90c2acf4b8,'0.999E1',18(18)>, total: #<BigDecimal:7f90c2acf1e8,'0.1098E1',18(18)>, state: "payment", adjustment_total: #<BigDecimal:7f90c2ace770,'0.0',9(18)>, user_id: nil, completed_at: nil, bill_address_id: 1814, ship_address_id: 1815, payment_total: #<BigDecimal:7f90c2acc358,'0.0',9(18)>, shipping_method_id: nil, shipment_state: nil, payment_state: nil, email: nil, special_instructions: nil, created_at: "2015-09-10 22:31:23", updated_at: "2015-09-10 22:33:03", currency: "USD", last_ip_address: "127.0.0.1", created_by_id: nil, shipment_total: #<BigDecimal:7f90c4b721e8,'0.199E1',18(18)>, additional_tax_total: #<BigDecimal:7f90c4b71fb8,'0.0',9(18)>, promo_total: #<BigDecimal:7f90c4b71e78,'0.0',9(18)>, channel: "spree", included_tax_total: #<BigDecimal:7f90c4b71798,'0.0',9(18)>, item_count: 1, approver_id: nil, approved_at: nil, confirmation_delivered: false, considered_risky: false>
There is a record on Spree::Order called item_count that needs to be > 0, but I don't know what the syntax needs to be in the controller?
#orders = Spree::Order.all.where(:item_count > 0) #this returns the following error: comparison of Symbol with 0 failed
This seems simple, but any help would be greatly appreciated! Thanks!
You can use symbol to check for absolute value, like where(item_count: 0), but not for comparison like that.
Inside where, when you are using symbols, you are actually working on a Hash object.
.where(item_count: 0)= .where({item_count: 0})
and to work with a hash, you should have key-value pair like above, not comparison. {:item_count > 0} is an invalid hash syntax.
For comparison in your case, use string:
#orders = Spree::Order.where('item_count > 0').all
Also, you should use .all, at the end of relation, if all needed. all returns the scope object and is usually meant to fetch all the records matching the query before it.

Rails - how to fetch from ActiveRecord object only specific records?

I get this from an ActiveRecord call:
#<ActiveRecord::Associations::CollectionProxy [
#<CarService id: nil, car_id: nil, car_service: 1,
created_at: nil, updated_at: nil, car_type: 0>,
#<CarService id: nil, car_id: nil, car_service: 11,
created_at: nil, updated_at: nil, car_type: 1>]>
Once I get this, I need to filter only records where car_type = "0". How to do that without doing another database call (WHERE car_type = "0")?
Thank you in advance.
EDIT:
this:
car.car_services.select{|key, hash| hash['car_type'] == "1" }
does not work.
just convert your result to an array then filter it like this
result = car.car_services.to_a.select do |e|
e.car_type == "0"
end
You can use scope in CarService model:
scope :type_ones, -> { where(car_type: 1) }
and you can use it like this:
car.car_services.type_ones
If you use enum, it will be better. Because the enum creates to scopes automatically instead of you. And of course it has more features. More about the enum.

ActiveRecord get average from group

I am trying to write this query in ActiveRecord 4, but to no avail.
SELECT date, AVG(gain) AS avg_gain FROM counters WHERE ( date > '2014-03-03' ) GROUP BY date ORDER BY date DESC;
So I scrambled this together:
Counter.select("date, AVG(gain) as avg_gain").where("date > '2014-03-03'").group(:date).order(date: :desc)
=> #<ActiveRecord::Relation [#<Counter id: nil, date: "2014-04-01">, #<Counter id: nil, date: "2014-03-31">, #<Counter id: nil, date: "2014-03-30">, #<Counter id: nil, date: "2014-03-29">, #<Counter id: nil, date: "2014-03-28">, #<Counter id: nil, date: "2014-03-27">, #<Counter id: nil, date: "2014-03-26">, #<Counter id: nil, date: "2014-03-25">, #<Counter id: nil, date: "2014-03-24">, #<Counter id: nil, date: "2014-03-23">, ...]>
The only trouble is, the result does not contain avg_gain columns. Any ideas?
When you use select, the selected fields are added to the instances in the returned Relation. The returned result does not include those selected fields in the printed output.
Access these fields just as you would access a column attribute:
result = Counter.select("date, AVG(gain) as avg_gain").where("date > '2014-03-03'").group(:date).order(date: :desc)
result.first.avg_gain # Prints expected value for the first Counter
OP wants to see hash values for the items queried. Here are two options, but you should just use the first, the second is here as more of a warning or option when performance doesn't matter:
I'd highly recommend this option, due to performance reasons:
Counter.
where("date > '2014-03-03'").
group(:date).
order(date: :desc).
pluck("date, AVG(gain) as avg_gain").
map { |date, avg_gain| {date: date, avg_gain: avg_gain} }
The solution below looks nice but is about 10x slower. This is because as_json will instantiate a model object for every result:
The Active Model JSON Serializer method, as_json will also return an array of hashes. By default, each result will also include "id"=>nil if you don't use the only: except: option.
Counter.
select("date, AVG(gain) as avg_gain").
where("date > '2014-03-03'").
group(:date).
order(date: :desc).
as_json(except: [:id])

Rails 3: use contents of an array as variables in a where method

I have three models: Isbn, Sale and Channel.
Aim: to get a list in the isbns show.html.erb view which looks something like this:
Isbn: myisbn
Total sales for myisbn: 100
Myisbn sales for channel 1: 50
Myisbn sales for channel 2: 25
Myisbn sales for channel 3: 25
Here are my models.
Isbn.rb model
has_many :sales
has_many :channels, :through => :sales
Sale.rb model (has attributes sales_channel_id, isbn_id, quantity)
has_many :channels
belongs_to :isbn
Channel.rb model:
belongs_to :sale
I've been working in the isbns controller, in the show method, just to get something to work. I thought I'd refactor later - advice on whether any of this stuff should go in the model would be most welcome.
So far I've got this:
#channelisbn = Sale.where("sales_channel_id =?',1).where("isbn_id=?",3)
#channelsalesisbn = 0
#channelisbn.each {|y| #channelsalesisbn =+ y.quantity}
This successfully gets all the sales where Channel ID is 1 and ISBN id is 3. But it's not much use, as the IDs are hard coded. So I got the Channel IDs into an array:
#channellist = Channel.all
#channel = 0
#channelarray = #channellist.map {|z| #channel = z.id}
which gives me a lovely array of [1,2,3,4]
But I can't figure out how to pass the 1, then the 2, then the 3 and then the 4 into a block which can be used to look up an ISBN's sales which have that sales channel id. This is what I tried (still hardcoding the ISBN id - thought I'd tackle one problem at a time), which returned an empty array:
#channelarray.each do |channel|
#channelisbn = []
#channelisbn = Sale.where("sales_channel_id = ?", channel).where("isbn_id = ?",3)
#channelsalesisbn = 0
#result = []
#result << #channelisbn.each {|a| #channelsalesisbn =+ a.quantity}
end
I was then going to sum the contents of the array.
Any help would be gratefully received. This is my first post, so my zero acceptance rate will change soon!
UPDATE
Just to finish this question off, here's where I've ended up, which is great, and ready for tinkering with: an array, nicely grouped, giving me sales by isbn by channel. Thanks for the group_by tip off!
#in the show action in the isbns controller:
#isbn = Isbn.find(params[:id])
#channelarray = Channel.select(:id).all
#channelarray.group_by {|i| Sale.where("channel_id = ?",i).where("isbn_id =?", #isbn)}
From the console, line breaks added for clarity:
(sneakily set #isbn = 3 first of all, since in the console you can't pass params from a view, so the #isbn instance defined in the controller is nil in the console)
ruby-1.9.2-p180 :067 > #channelarray.group_by {|i| Sale.where("channel_id = ?",i).where("isbn_id =?", #isbn)}
=> {[#<Sale id: 1, isbn_id: 3, quantity: 10000, value: 12000, currency: "GBP", total_quantity: nil, created_at: "2011-05-06 12:30:35", updated_at: "2011-05-07 17:43:13", customer: "Waterstone's", retail_price: nil, discount: nil, invoice_date: "2011-05-24">, #<Sale id: 2, isbn_id: 3, quantity: 1000, value: 500, currency: "GBP", total_quantity: nil, created_at: "2011-05-07 09:37:53", updated_at: "2011-05-07 19:14:52", customer: "Borders", retail_price: nil, discount: nil, invoice_date: "2011-02-05">]=>[#<Channel id: 1>],
[#<Sale id: 3, isbn_id: 3, quantity: 500, value: 1500, currency: "", total_quantity: nil, created_at: "2011-05-07 09:38:11", updated_at: "2011-05-07 19:15:07", customer: "Borders", retail_price: nil, discount: nil, invoice_date: "2011-12-05">, #<Sale id: 4, isbn_id: 3, quantity: 45, value: 300, currency: "", total_quantity: nil, created_at: "2011-05-07 09:38:38", updated_at: "2011-05-07 19:15:36", customer: "Borders", retail_price: nil, discount: nil, invoice_date: "2011-06-05">]=>[#<Channel id: 2>],
[]=>[#<Channel id: 3>],
[]=>[#<Channel id: 4>]}
UPDATE 2
Ha, the hash I generated had the key value pairs the wrong way round. The array containing the sales data was the key - it should have been the value. Rubydocs saved the day:
#salesbychannel = #salesbychannelwrong.invert
The invert method switches the key-value pairs. Sweet.
What you're looking for is passing an array to a ARel#where(), like this:
Sale.where(:sales_channel_id => #channelarray)
This should execute an IN query. If that's not working, you can always pass the array to ActiveRecord#find, like this:
Sale.find(#channelarray)
Hope this helps

Finding elements in array of hashes

I'm trying to build a model of products which has many components. Some components are optional and depend on the choice the user is making to enable them or not.
I have two models, one is configuration and the other is elements (of that configuration).
At the beginning I bring all the elements of the array, and then create another array of those which will be shown by default.
But when I write the following code it gives me an error despite both objects being arrays of hashes.
So I bring my first array of all elements:
irb(main):252:0* #all = Configuration.find(1).elements
=> [#<Element id: 1, name: "elem1", quantity: 1, position: 1, subposition: nil, created_at: nil, updated_at: nil, configuration_id: 1>, #<Element id: 2, name: "elem2", quantity: 2, position: 2, subposition: 1, created_at: nil, updated_at: nil, configuration_id: 1>, #<Element id: 3, name: "elem3", quantity: 3, position: 2, subposition: 2, created_at: nil, updated_at: nil, configuration_id: 1>, #<Element id: 4, name: "elem4", quantity: 4, position: 3, subposition: nil, created_at: nil, updated_at: nil, configuration_id: 1>]
Then I filter to be only those that have a subposition nil or 1
irb(main):253:0> #default = #all.where(:subposition=>nil).concat(#all.where(:subposition=>1))
=> [#<Element id: 1, name: "elem1", quantity: 1, position: 1, subposition: nil, created_at: nil, updated_at: nil, configuration_id: 1>, #<Element id: 4, name: "elem4", quantity: 4, position: 3, subposition: nil, created_at: nil, updated_at: nil, configuration_id: 1>, #<Element id: 2, name: "elem2", quantity: 2, position: 2, subposition: 1, created_at: nil, updated_at: nil, configuration_id: 1>]
So far so good, as you can see, Elem3 is not being shown in #default as it doesn't meet the requiements.
The problem comes when I try to play with the arrays as I need to perform certain operations.
irb(main):257:0> #all.where(:position =>1)
=> [#<Element id: 1, name: "elem1", quantity: 1, position: 1, subposition: nil, created_at: nil, updated_at: nil, configuration_id: 1>]
But the same operation in #default will fail,
irb(main):258:0> #default.where(:position =>1)
NoMethodError: undefined method `where' for #<Array:0x2641660>
Now, they're both arrays of hashes and look the same, why is the same method failing in the second case?
Thanks a lot for your help!
Throughout your code, #all is an ActiveRecord::Relation, not an array. This lets you perform the standard .where call (among others). When you assigned to #default, you used .concat which evaluated the query and assigned an actual array to #default.
You might try a different approach in your second code block. Maybe something like this:
#default = #all.where("subposition is null or subposition = ?", 1)
Well, your problem is that concat transforms a collection into an array.
I'd replace:
irb(main):253:0> #default = #all.where(:subposition=>nil).concat(#all.where(:subposition=>1))
by:
#default = #all.where("subposition = '1' OR subposition = nil") #I'm unsure of the nil in the statement, I nerver remember, try NULL if it fails
This way, you make only one db query and you keep an ActiveRecord collection.
Thus, you'll be able to chain other where conditions on it.

Resources