sorry about the title, cannot express myself better.
I have this:
{
7758 => { 3259 => 10, 39625 => 10, 36410 => 20, 36238 => 20, 34951 => 20, 32101 => 10},
7916 => { 3259 => 10, 39625 => 10, 36410 => 20, 36238 => 20, 34951 => 20, 32101 => 10},
8857 => { 1000 => 10, 39625 => 10 }
}
the keys of those hashes represents record ids, the values represent the data that should go in their row attribute.
problem is that this hash can weigh a lot, so it's imprudent to issue an update on the database for each record represented in the hash.
instead I thought about grouping up hashes with identical values and have a structure for which I can just issue an update to records in one shot.
comparing hashes values can be done even by transforming the nested hashes themselves into a json string, since it's the datatype we use for that column.
in the end I'd like to issue an update_all for a series of variants that has the same hash content, I understand that the number of updates issues to the database is 1:1 to how unique are the hash values, but I kinda have the choice to sort their keys in someway before the comparison should we have something smart to compare existing values rather than converting the content to a string for comparison purposes.
what happens now is a normal update on each hash record in a cycle:
UPDATE "table" SET "rates" = '{"3259":10,"39625":10,"36410":20,"36238":20,"34951":20,"32101":10}', WHERE "table"."variant_id" = 7758
UPDATE "table" SET "rates" = '{"3259":10,"39625":10,"36410":20,"36238":20,"34951":20,"32101":10}', WHERE "table"."variant_id" = 7916
UPDATE "table" SET "rates" = '{"1000":10,"39625":10}' WHERE "table"."variant_id" = 7916
I'd like to transform the original structure in something that allows me to perform this:
UPDATE "table" SET "rates" = '{"3259":10,"39625":10,"36410":20,"36238":20,"34951":20,"32101":10}', WHERE "table"."variant_id" IN(7758, 7916)
UPDATE "table" SET "rates" = '{"1000":10,"39625":10}' WHERE "table"."variant_id" = 7916
I tried a
hash.group_by { |h| h[1].to_json }.each do |rate|
but I have this in rate:
["{\"3259\":10,\"39625\":10,\"36410\":20,\"36238\":20,\"34951\":20,\"32101\
":10}", [[7758, {3259=>10, 39625=>10, 36410=>20, 36238=>20, 34951=>20,
32101=>10}], [7916, {3259=>10, 39625=>10, 36410=>20, 36238=>20,
34951=>20, 32101=>10}], [8857, {3259=>10, 39625=>1...
Maybe something like this:
result = hash.each_with_object({}) do |(id, attributes), result|
json_string = attributes.to_json
result[json_string] ||= []
result[json_string] << id
end
result.each do |json_string, ids|
# ...
end
Related
I have an array of hashes lets say offers = {offer1,...,offer6}. Each offer is a hash.
Each offer has various keys one of which is a price hash:
offer1 = {..., :price => {:amount => 400, :amount2 => 300, :currency => "INR",...}
Now I want to return a hash with only the unique hashes/offers. Also, I want :price hash to have the values averaged.
So, the final offers array will have:
offers=[offer1, offer2,...]
offer1[:price] will have values equal to average of the values of each key from duplicated offer hashes and same with offer2[:price] and so on so that in end I end with an hash with only unique offers.
offers1..6 can be duplicate with same id and price hash different. If they are duplicate we need to do the averaging otherwise not.
Is there an elegant way to do all of this?
I have tried grouping the hashes with a unique key and merging the price hashes of each. But am unable to reach the final solution.
my attempt:
rooms_hash = rooms.map do |room|
unless room[:offers].uniq.count == room[:offers].count
grouped_offers = room[:offers].group_by{|x| x[:room_category_id]}
offer_values = grouped_offers.values
price_array = offer_values.map do |v|
v.inject do |k, v|
k.merge!(price: k[:price].merge(v[:price]){|_, a, b| [a, b].flatten })
end
end
price_array.map do |o|
o[:price] = {}.tap{ |h| o[:price].each {|k, list| h[k] = list.all?{|e| [Fixnum, NilClass].include? e.class} ? list.map(&:to_i).sum/list.size : list.compact.first ; h } }
end
price_array
end
end
rooms.zip(rooms_hash).map do |room,averaged_offers|
if room[:offers].count > 1
room[:offers] = averaged_offers.select{|offer| offer[:room_category_id] == room[:room_category_id]}
room
end
end
This code actually fails when I don't get any duplicates. So how can I check for that as well?
Edit:
Yes offers is an array and offers1..6 are hashes.
offer1..._hash = {:offer_id=>"uuid", :price=>{:amount=>4422380, :gross_amount=>4422380, :currency=>"INR", :tax=>434318, :hotel_fees=>0, :base_fare=>3988062}, .....}
I am trying to create the back-end for a calendar system. The calendar is just a list of Events. I am trying to organise this in to a reasonable JSON response. The structure I am looking to replicate would be something like this
eventsList = [
{ 'year' => 2014,
'events' => [{event data hash 1},
{event data hash 2}]
},
{ 'year' => 2015,
'events' => [{event data hash 1},
{event data hash 2}]
}
]
I am having trouble trying to add events to the right array. Below I have Event.all and I am trying to sort the list.
events = Event.all
eventList = []
events.each do |event|
#Creates a hash of the current event info
eventInfo = {'description' => event.description, 'startdate' => event.startdate}
eventMonthNumber = event.startdate.strftime('%m').to_i
eventMonthName = event.startdate.strftime('%B')
eventYearNumber = event.startdate.strftime('%Y').to_i
# Adds year to eventList if it isn't present
unless eventList.include?(eventYearNumber)
eventList << {'year' => eventYearNumber, 'events' => []}
end
# Tries to find current year hash in array and add to event key
currentYear = eventList.select {|event| event['year'] == eventYearNumber}
currentYear['events'] << eventInfo
end
I get the error no implicit conversion of String into Integer. I am not entirely sure whether the eventList.select is the correct way to go about this
Although I have moved away from this current structure the change that made it work was swapping out
eventList.select
For
eventList.detect
Select returns an array (?) And detect returns one item.
I am trying this for the first time and am not sure I have quite achieved what i want to. I am pulling in data via a screen scrape as arrays and want to put them into a hash.
I have a model with columns :home_team and :away_team and would like to post the data captured via the screen scrape to these
I was hoping someone could quickly run this in a rb file
require 'open-uri'
require 'nokogiri'
FIXTURE_URL = "http://www.bbc.co.uk/sport/football/premier-league/fixtures"
doc = Nokogiri::HTML(open(FIXTURE_URL))
home_team = doc.css(".team-home.teams").map {|team| team.text.strip}
away_team = doc.css(".team-away.teams").map {|team| team.text.strip}
team_clean = Hash[:home_team => home_team, :away_team => away_team]
puts team_clean.inspect
and advise if this is actually a hash as it seems to be an array as i cant see the hash name being outputted. i would of expected something like this
{"team_clean"=>[{:home_team => "Man Utd", "Chelsea", "Liverpool"},
{:away_team => "Swansea", "Cardiff"}]}
any help appreciated
You actually get a Hash back. But it looks different from the one you expected. You expect a Hash inside a Hash.
Some examples to clarify:
hash = {}
hash.class
=> Hash
hash = { home_team: [], away_team: [] }
hash.class
=> Hash
hash[:home_team].class
=> Array
hash = { hash: { home_team: [], away_team: [] } }
hash.class
=> Hash
hash[:hash].class
=> Hash
hash[:hash][:home_team].class
=> Array
The "Hash name" as you call it, is never "outputed". A Hash is basically a Array with a different index. To clarify this a bit:
hash = { 0 => "A", 1 => "B" }
array = ["A", "B"]
hash[0]
=> "A"
array[0]
=> "A"
hash[1]
=> "B"
array[1]
=> "B"
Basically with a Hash you additionally define, how and where to find the values by defining the key explicitly, while an array always stores it with a numerical index.
here is the solution
team_clean = Hash[:team_clean => [Hash[:home_team => home_team,:away_team => away_team]]]
Im trying to arrange #plrdet by the values in arr.
when im selecting this way:
#plrdet = Player.find_all_by_fid(arr)
it returns in the order of the rows in the table, i want it to be ordered by the order of arr.
for example:
Player contains the following attributes: address, age, uniqnum.
and:
arr
is an array of the uniqnum.
arr=[456,123,789]
player=[{NYC,32,123},{BSAS,27,456},{LND,30,789})
the result that im looking for should be from the "find_all"
player=[,{BSAS,27,456},{NYC,32,123},{LND,30,789})
If I understand the problem I would try something like this:
Hash version
players = [{}]
#plrdet.each do |player|
players << {"adress" => player.adress, "age" => player.age, "fid" => player.fid}
end
players.inspect
Now result should be [{"adress" => BSAS, "age" => 27, "fid" => 456},{"adress" => NYC, "age" => 32,"fid" => 123},{"adress" => LND, "age" => 30, "fid" => 789}]
Array version
players = [[]]
#plrdet.each do |player|
players << [player.adress, player.age, player.fid]
end
Now result should be [[BSAS,27,456],[NYC,32,123],[LND,30,789]]
Sort
I think this solution should work but I don't like it and there are maybe better way to solve your problem :
sorted_players = [[]]
arr.each do |arr_fid|
sorted_players << players.collect{|player| player if player.include?(arr_fid)}
end
You have two options:
Use order to sort the results with the query
Use sort to sort the results in memory
You may use 1. It will be something like:
#plrdet = Player.find_all_by_fid(arr).order("address")
I have a table with columns 'id', 'resource_id', 'read_time', 'value' where 'value' is a float
What I am trying to accomplish is to return a list of records such that the 'value' of each record is the sum of all the records at a specific 'read_time' but having differing 'resource_id' values.
I am wondering if there is a clever way (ie not looping through all the entries) to accomplish this. Currently I am implementing something along these lines:
#aggregate_meters = []
#res_one_meters = Meter.find(:all, :conditions => ["resource_id = ?", 1])
#res_one_meters.each do |meter|
read_time = meter.read_time
value = meter.value
if res_two_meter = Meter.find(:first, :conditions => ["resource_id = ? AND read_time = ?", 2, read_time ])
value = value + res_two_meter.value
end
aggregate_meter = Meter.new(:read_time => read_time, :value => value, :resource_id => 3)
#aggregate_meters.push(aggregate_meter)
end
Thank you.
ActiveRecord::Calculate is your friend here. Letting you do exactly what you want with one database call. It returns a hash using the unique values in the column used in the group as keys.
Here's the code you wrote, rewritten to use sum.
values = Meter.sum(:value, :group => :read_time)
values.each do |read_time, value|
aggregate_meter = Meter.new(:read_time => read_time, :value => value, :resource_id => 3)
#aggregates_meter.push(aggregate_meter)
end