iterate over an array of hashes to create data in rails - ruby-on-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

Related

Rails ActiveRecord join table with array of ids without n+1 request

I need to retrieve records from an array of Ids like:
User.where(id: [1,1,2])
BUT the problem is that I only get two records from this request and I want to have the [#User id:1] in double as number 1 appears twice in the array.
I could do a n+1 request but that's not very optimistic...
The steps you should follow if you want a single simple query to database:
Create a hash based on the array like:
hash = {1=>2, 2=>1, 3=>1, 4=>3} for [1, 1, 2, 3, 4, 4, 4]
Query simply:
users = User.where(id: your_array.uniq)
# Your case
users = User.where(id: [1, 1, 2].uniq) # because [1, 1, 2].uniq => [1, 2]
make your array:
the_way_i_want = []
hash.each do |key, value|
value.times do
the_way_i_want.push users.where(id: key)
end
end
set the users = the_way_i_want and return
users = the_way_i_want
crude but will save database access :-)
How about creating 1 request to DB, and then just format your data
user_ids = [1, 1, 2] # this you have
Create a hash { user_id: user_record, .... }
users = User.where(id: user_ids).map { |u| [u.id, u]}.to_h
Then create an array with all the records you want
user_ids.map do |u_id|
users[u_id]
end

Rails group by column and select column

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 :)

Ruby on Rails: Get specific substring out of string

a little help with getting data out of a string.
Assuming I executed a sql query and now have a string(which set as hash on db):
"{\"users_associated\":{\"User:4\":6,\"User:22\":28,\"User:30\":36}}"
(Which stands for User:ID : User.display_id)
How can I get a substring the includes all users ids or all their display ids, so I'll have something like 4,22,30 or 6,22,36)?
Thanks!
It's common for data systems to return data in a serialized form, i.e. using data types that facilitate transmission of data. One of these serializable data types is String, which is how your JSON data object has been received.
The first step would be to de-serialize (or parse) this String into a Hash object using JSON.parse and tease out just the data value for key "users_associated".
your_string = "{\"users_associated\":{\"User:4\":6,\"User:22\":28,\"User:30\":36}}"
hash = JSON.parse(your_string)
data = hash["users_associated"]
#=> {"User:4":6, "User:22": 28, "User:30": 36}
Hash#keys gives you an array of a hash's keys.
Hash#values gives you an array of a hash's data values.
keys = data.keys
#=> ["User:4", "User:22", "User:30"]
values = data.values
#=> [6, 28, 36]
Array#join lets you string together the contents of an array with a defined separator, , in this case.
display_ids = keys.join(',')
#=> "6,28,36"
For the User IDs, you could Array#map every element of the values array to replace every string occurrence of "User:" with "", using String#gsub.
user_ids = values.map{|user_id| user_id.gsub("User:", "")}
#=> ["4", "22", "30"]
Then, in a similar way to display_ids, we can Array#join the contents of the user_ids array to a single string.
user_ids = user_ids.join(",")
#=> "4,22,30"
You can create two helper methods. I'm leaving return values as arrays because I assume you would need to iterate on them at some point and also converting the user id's to integers.
def extract_display_ids(json)
json['users_associated'].values
end
def extract_user_ids(some_data)
json['users_associated'].keys.map{ |key| key.split(':').last.to_i }
end
some_data = JSON.parse("{\"users_associated\":{\"User:4\":6,\"User:22\":28,\"User:30\":36}}")
extract_display_ids(some_data)
#=> [6, 28, 36]
extract_user_ids(some_data)
#=> [4, 22, 30]
If possible though, I would recommend trying to get a better data format:
{ users_associated:
[{ user_id : 4, display_id:6 }, { user_id : 4, display_id:6 }]
}
I wrote class for this. If you want, you can add it to your project and use it as follows:
require 'json'
class UserSubstringExtractor
def initialize(user_json_data)
#user_json_data = user_json_data
end
def display_ids
user_data.dig('users_associated').values
end
def user_ids
user_data.dig('users_associated').keys.map { |u| u.split(':').last.to_i }
end
private
def user_data
JSON.parse(#user_json_data)
end
end
user_json_data = '{"users_associated":{"User:4":6,"User:22":28,"User:30":36}}'
extractor = UserSubstringExtractor.new(user_json_data)
p extractor.display_ids
#=> [6, 28, 36]
p extractor.user_ids
#=> [4, 22, 30]

What is the best way to access an element from 2d array saved as a hash value?

I have a hash, its values are 2 dimensional arrays, e.g.
hash = {
"first" => [[1,2,3],[4,5,6]],
"second" => [[7,88,9],[6,2,6]]
}
I want to access the elements to print them in xls file.
I did it in this way:
hash.each do |key, value|
value.each do |arr1|
arr1.each do |arr2|
arr2.each do |arr3|
sheet1.row(row).push arr3
end
end
end
end
Is there a better way to access each single element without using each-statement 4 times?
The desired result is to get each value from key-value pair as an array, e.g.
=> [1,2,3,4,5,6] #first loop
=> [7,88,9,6,2,6] #second loop
#and so on
hash = { "first" =>[[1, 2,3],[4,5,6]],
"second"=>[[7,88,9],[6,2,6]] }
hash.values.map(&:flatten)
#=> [[1, 2, 3, 4, 5, 6], [7, 88, 9, 6, 2, 6]]
Isn't it as simple as something like:
hash.each do |k,v|
sheet1.row(row).concat v.flatten
end

How can I do something like this with ruby arrays

I have an user array like this:
users_array = [[1,text for 1],[2,text for 2],[3,text for 3],[4,text for 4],[5,text for 5]]
here first element is user_id and second element is text which is specific to user_id in the same array.
Now I am trying to have user object from instead of ids in array like these.
users_array = [[#<User id: 1, encrypted_email: "">,text for 1],[#<User id: 2, encrypted_email: "">,text for 2],[#<User id: 3, encrypted_email: "">,text for 3],[#<User id: 4, encrypted_email: "">,text for 4],[#<User id: 5, encrypted_email: "">,text for 5]]
I am trying not to loop the array and hit the db thousand times for thousands user.
data = users_array.to_h
# find all users with single query and build your map
User.where(id: data.keys).map { |user| [user, data[user.id]] }
You could use transpose to extract ids and values, and zip to combine users and values:
ids, values = users_array.transpose
users_array = User.find(ids).zip(values)

Resources