Rails group by column and select column - ruby-on-rails

I have a table DinnerItem with columns id, name, project_id, client_id, item_id and item_quantity.
I want to fetch data group_by item_id column and the value should only have the item_quantity column value in the format
{ item_id1 => [ {item_quantity from row1}, {item_quantity from row2}],
item_id2 => [ {item_quantity from row3}, {item_quantity from row4} ]
}
How can I achieve it in one single query?
OfferServiceModels::DinnerItem.all.select('item_id, item_quantity').group_by(&:item_id)
But this has the format
{1=>[#<DinnerItem id: nil, item_id: 1, item_quantity: nil>, #<DinnerItem id: nil, item_id: 1, item_quantity: {"50"=>30, "100"=>10}>], 4=>[#<DinnerItem id: nil, item_id: 4, item_quantity: {"100"=>5, "1000"=>2}>}

Something like this should do the job:
result = OfferServiceModels::DinnerItem
.pluck(:item_id, :item_quantity)
.group_by(&:shift)
.transform_values(&:flatten)
#=> {1 => [10, 20], 2 => [30, 40]}
# ^ item id ^^ ^^ item quantity
A step by step explanation:
# retrieve the item_id and item_quantity for each record
result = OfferServiceModels::DinnerItem.pluck(:item_id, :item_quantity)
#=> [[1, 10] [1, 20], [2, 30], [2, 40]]
# ^ item id ^^ item quantity
# group the records by item id, removing the item id from the array
result = result.group_by(&:shift)
#=> {1 => [[10], [20]], 2 => [[30], [40]]}
# ^ item id ^^ ^^ item quantity
# flatten the groups since we don't want double nested arrays
result = result.transform_values(&:flatten)
#=> {1 => [10, 20], 2 => [30, 40]}
# ^ item id ^^ ^^ item quantity
references:
pluck
group_by
shift
transform_values
flatten

You can keep the query and the grouping, but append as_json to the operation:
DinnerItem.select(:item_id, :item_quantity).group_by(&:item_id).as_json
# {"1"=>[{"id"=>nil, "item_id"=>1, "item_quantity"=>1}, {"id"=>nil, "item_id"=>1, "item_quantity"=>2}],
# "2"=>[{"id"=>nil, "item_id"=>2, "item_quantity"=>1}, {"id"=>nil, "item_id"=>2, "item_quantity"=>2}]}
Notice as_json will add the id of each row which will have a nil value.

I don't know that this is possible without transforming the value returned from the db. If you are able to transform this, the following should work to give you the desired format:
OfferServiceModels::DinnerItem.all.select('item_id, item_quantity').group_by(&:item_id)
.transform_values { |vals| vals.map(&:item_quantity) }
# => {"1"=>[nil,{"50"=>30, "100"=>10}],"4"=>...}
# or
OfferServiceModels::DinnerItem.all.select('item_id, item_quantity').group_by(&:item_id)
.transform_values { |vals| vals.map { |val| val.slice(:item_quantity) }
# => {"1"=>[{:item_quantity=>nil},:item_quantity=>{"50"=>30, "100"=>10}}],"4"=>...}
I'd argue there's nothing wrong with the output you're receiving straight from the db though. The data is there, so output the relevant field when needed: either through a transformation like above or when iterating through the data.
Hope this helps in some way, let me know :)

Related

Search by array of values in .select query method

I'm having this type of search:
values = ModelName.find(:all, :conditions => ['attr_id IN (SELECT attr_id FROM srv_type_attr WHERE id IN (?))', serv_objt_attr.collect(&:stya_id)])
Witch returns me an array of needed values:
[33458, 33438]
Next i need to check if record exists with select:
serv_objt_attr.select {|array| array.stya_id == values.collect(&:attr_id).uniq}
This is an example what i'm thinking off.
So how to do it with select, so he would walk through all values witch i'm getting from values.
I know that i could to something like
values.collect(&:attr_id).uniq do |val|
serv_objt_attr.select {|array| array.stya_id == val}
end
But i do not thing that this is a good option.
Ruby 1.8.7
Rails 2.3.4
This is a good case for the set intersection operator:
values = ModelName.find(:all, :conditions => ['attr_id IN (SELECT attr_id FROM srv_type_attr WHERE id IN (?))', serv_objt_attr.collect(&:stya_id)])
values & Set.new(serv_objt_attr.map(&:stya_id)
Here's what the & does:
>> values = [1,2,3]
=> [1, 2, 3]
>> other_array = [1,5,9,3]
=> [1, 5, 9, 3]
>> values & other_array
=> [1, 3]

Compare 2 ActiveRecord Relations rails

I have an interest form which is used to add an interest to a user. The structure of the database is as follows:
Users - id ...
Interests - id ..
UsersMapInterests - user_id interest_id
So when a user selects an interest from the Interests table, it gets added to the UsersMapInterests table.
A query for user 1 would return something like {{user_id: 1, interest_id 3},{user_id: 1, interest_id 7},{user_id: 1, interest_id 25}}.
A query for user 22 would return {user_id: 22, interest_id 3},{user_id: 22, interest_id 3}
Now I would like to compare the results with each other.
I assume that using to_a and comparing them like this would be too much:
user1.each do |int1|
user22.each do |int2|
if int1.interest_id == int2.interest_id
count++
end
end
end
How else could I compare them?
You can just use the following line of code
(user1.interest_ids & user22.interest_ids).count
Explanation:
user1.interest_ids
#=> [3, 7, 25]
user22.interest_ids
#=> [3, 3]
[3, 7, 25] & [3, 3] # Returns common element from both arrays
#=> [3]
[3].count
#=> 1
I will do something like this:
user1_interest_ids = user1.map { |o| o[:interest_id] }
user22_interest_ids = user22.map { |o| o[:interest_id] }
count = (user1_interest_ids & user22_interest_ids).size

iterate over an array of hashes to create data in rails

I have an array of hashes that I am trying to seed into a database.
shoe_array = [{:department_id=>8, :follower_id=>31}, {:department_id=>9, :follower_id=>41}, {:department_id=>4, :follower_id=>49}, {:department_id=>2, :follower_id=>58}, {:department_id=>5, :follower_id=>36}, {:department_id=>9, :follower_id=>63}, {:department_id=>2, :follower_id=>52}, {:department_id=>23, :follower_id=>26}, {:department_id=>5, :follower_id=>52}, {:department_id=>6, :follower_id=>30}]
shoe_array.each do |n, k|
department_id = n,
follower_id = k,
user_id = 1
Relationship.create!(department_id: department_id,
follower_id: follower_id,
user_id: user_id)
end
I'm only getting null values for both department_id and follower_id. user_id is working.
I have tried using "#{n}" and "#{k}", to get the key values set to department and follower ids. I've also tried iterating over the array only using .each do |a| and setting department_id: a['department_id'], follower_id a['follower_id']
as seen here: iterate through array of hashes in ruby and here :How do I iterate over an array of hashes and return the values in a single string?
but I'm only still getting null values. How can I get my values into the database?
shoe_array is an array of hashes, so you should iterate over each hash, and access each key-value pair:
shoe_array.each do |hash|
department_id = hash[:department_id]
follower_id = hash[:follower_id]
user_id = 1
Relationship.create!(
department_id: department_id,
follower_id: follower_id,
user_id: user_id
)
end
According the documentation you can create records from an array of hashes:
Following should work (You can use create! as well as create)
shoe_array = [{:department_id=>8, :follower_id=>31}, {:department_id=>9, :follower_id=>41}, {:department_id=>4, :follower_id=>49}, {:department_id=>2, :follower_id=>58}, {:department_id=>5, :follower_id=>36}, {:department_id=>9, :follower_id=>63}, {:department_id=>2, :follower_id=>52}, {:department_id=>23, :follower_id=>26}, {:department_id=>5, :follower_id=>52}, {:department_id=>6, :follower_id=>30}]
Relationship.create!(shoe_array.map{|arr| arr.merge!({user_id: 1})})
Change your iteration to
shoe_array.each do |shoe|
department_id = shoe[:department_id]
follower_id = shoe[:follower_id]
An example that can use |n, k| would be either a hash or an array of arrays. If you want to go down that route, you can call values on each hash in the array (assuming that the hash is consistent, meaning department_id always comes first before follower_id)
ids = shoe_array.map(&:values) # [[8, 31], [9, 41], [4, 49], [2, 58], [5, 36], [9, 63], [2, 52], [23, 26], [5, 52], [6, 30]]
Then you can just use your old code or refactor to
ids.each do |department_id, follower_id|
Relationship.create!(
department_id: department_id,
follower_id: follower_id,
user_id: 1
)
end
Take note though that you are iterating over the array twice and will be less efficient compared to the first one.
UPDATE
Another option is use the array elements as is.
shoe_array.each do |attributes|
relationship = Relationship.new(attributes)
relationship.user_id = 1
relationship.save!
end

Ruby on Rails group_by is not grouping in the correct order using DateTime

First I get a collection of channels from my scope
Channel.not_archived.map { |c| channels << c }
Then I sort those by the start_time attribute:
channels.sort! { |a, b| a.start_time <=> b.start_time }
Then I want to group them by their start times. So channels that start at 8:00am will be grouped together. So I use the group_by method:
#grouped_channels = #channels.group_by { |c| time_with_offset(c).strftime("%I:%M %P") }
the time_with_offset method:
# Returns time with offset
def time_with_offset(channel)
user = current_user.time_zone.to_i
organization = channel.organization.time_zone.to_i
time_offset = organization -= user
channel.start_time - time_offset.hours
end
And I get back all of my records in the correct group. The issue i'm having is that the groups are not in order. So the group of 8:00am should be before the group of 9:00am. It's just in weird random order now. Can anyone help me get these in correct order?
If you wish to reorder the key-value pairs of any hash h in key order given by an array keys, which contains all the keys in the desired order, write
(keys.zip(h.values_at(*keys))).to_h
For Ruby versions prior to 2.0, write
Hash[keys.zip(h.values_at(*keys))]
For example,
h = { b: 1, d: 2, a: 3, c: 4 }
#=> {:b=>1, :d=>2, :a=>3, :c=>4}
keys = [:a, :b, :c, :d]
(keys.zip(h.values_at(*keys))).to_h
#=> {:a=>3, :b=>1, :c=>4, :d=>2}
The steps are as follows.
a = h.values_at(*keys)
#=> same as h.values_at(:a, :b, :c, :d)
#=> [3, 1, 4, 2]
b = keys.zip(a)
# => [[:a, 3], [:b, 1], [:c, 4], [:d, 2]]
b.to_h
#=> {:a=>3, :b=>1, :c=>4, :d=>2}
First you are sorting by one time, then you are grouping by a different time. I expect this explains your undesired order.
Sort by the offset time.
channels.sort_by { |c| time_with_offset(c) }.group_by { |c| time_with_offset(c).strftime("%I:%M %P") }

How to group by two conditions in rails 3 and loop through them

Ok so I have a sale model that
recent_sales = Sale.recent
=> [#<Sale id: 7788, contact_id: 9988, purchasing_contact_id: 876, event_id: 988, #<BigDecimal:7fdb4ac06fe8,'0.0',9(18)>, fulfilled_at: nil, skip_print: nil, convention_id: 6, refund_fee: #<BigDecimal:7fdb4ac06de0,'0.0',9(18)>, processing: false>, #<Sale id: 886166, , contact_id: 7775,
recent_sales.count
=> 32
I know i can do this
grouped_sales = recent_sales.group_by(&:contact_id).map {|k,v| [k, v.length]}
=> [[9988, 10], [7775, 22]]
But what i really need is not just grouping on contact_id but also event_id so the final results looks like this
=> [[9988, 988, 5], [9988, 977, 5], [7775, 988, 2], [7775, 977, 20]]
so i have the event_id and the grouping is splitting them up correctly...any ideas on how to do this
I tried
recent_sales.group('contact_id, event_id').map {|k,v| [k, k.event, v.length]}
but no go
grouped_sales = recent_sales.group_by { |s| [s.contact_id, s.event_id] }
.map { |k,v| [k.first, k.last, v.length] }
Simply, try
group(['contact_id','event_id'])
It worked for me. So, I posted as answer to help others as well.
Ask the database to do the grouping
grouped_sales = recent_sales.group([:contact_id, :event_id]).count
the result is a hash each key is an array of the contact and event id, and the value is the count.
So if you want arrays of three
grouped_sales = recent_sales.group([:contact_id, :event_id]).count.map{ |k,v| k << v }

Resources