Adding to and deleting hashes - ruby-on-rails

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.

Related

ruby group by hashes' values in hash

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

Take array and convert to a hash Ruby

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]]]

ordering array by elements

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

Ruby way to loop and check subsequent values against each other

I have an array that contains dates and values. An example of how it might look:
[
{'1/1/2010' => 'aa'},
{'1/1/2010' => 'bb'},
{'1/2/2010' => 'cc'},
{'1/2/2010' => 'dd'},
{'1/3/2010' => 'ee'}
]
Notice that some of the dates repeat. I'm trying to output this in a table format and I only want to show unique dates. So I loop through it with the following code to get my desired output.
prev_date = nil
#reading_schedule.reading_plans.each do |plan|
use_date = nil
if plan.assigned_date != prev_date
use_date = plan.assigned_date
end
prev_date = plan.assigned_date
plan.assigned_date = use_date
end
The resulting table will then look something like this
1/1/2010 aa
bb
1/2/2010 cc
dd
1/3/2010 ee
This work fine but I am new to ruby and was wondering if there was a better way to do this.
Enumerable.group_by is a good starting point:
require 'pp'
asdf = [
{'1/1/2010' => 'aa'},
{'1/1/2010' => 'bb'},
{'1/2/2010' => 'cc'},
{'1/2/2010' => 'dd'},
{'1/3/2010' => 'ee'}
]
pp asdf.group_by { |n| n.keys.first }.map{ |a,b| { a => b.map { |c| c.to_a.last.last } } }
# >> [{"1/1/2010"=>["aa", "bb"]}, {"1/2/2010"=>["cc", "dd"]}, {"1/3/2010"=>["ee"]}]
Which should be a data structure you can bend to your will.
I don't know as though it's better, but you could group the values by date using (e.g.) Enumerable#reduce (requires Ruby >= 1.8.7; before that, you have Enumerable#inject).
arr.reduce({}) { |memo, obj|
obj.each_pair { |key, value|
memo[key] = [] if ! memo.has_key?(key);
memo[key] << value
}
memo
}.sort
=> [["1/1/2010", ["aa", "bb"]], ["1/2/2010", ["cc", "dd"]], ["1/3/2010", ["ee"]]]
You could also use Array#each to similar effect.
This is totally a job for a hash.
Create a hash and use the date as the hashkey and an empty array as the hashvalue.
Then accumulate the values from the original array in the hashvalue array

There has got to be a cleaner way to do this

I have this code here and it works but there has to be a better way.....i need two arrays that look like this
[
{
"Vector Arena - Auckland Central, New Zealand" => {
"2010-10-10" => [
"Enter Sandman",
"Unforgiven",
"And justice for all"
]
}
},
{
"Brisbane Entertainment Centre - Brisbane Qld, Austr..." => {
"2010-10-11" => [
"Enter Sandman"
]
}
}
]
one for the past and one for the upcoming...the problem i have is i am repeating myself and though it works i want to clean it up ...here is my data
..
Try this:
h = Hash.new {|h1, k1| h1[k1] = Hash.new{|h2, k2| h2[k2] = []}}
result, today = [ h, h.dup], Date.today
Request.find_all_by_artist("Metallica",
:select => "DISTINCT venue, showdate, LOWER(song) AS song"
).each do |req|
idx = req.showdate < today ? 0 : 1
result[idx][req.venue][req.showdate] << req.song.titlecase
end
Note 1
In the first line I am initializing an hash of hashes. The outer hash creates the inner hash when a non existent key is accessed. An excerpt from Ruby Hash documentation:
If this hash is subsequently accessed by a key that doesn‘t correspond to a hash
entry, the block will be called with the hash object and the key, and should
return the default value. It is the block‘s responsibility to store the value in
the hash if required.
The inner hash creates and empty array when the non existent date is accessed.
E.g: Construct an hash containing of content as values and date as keys:
Without a default block:
h = {}
list.each do |data|
h[data.date] = [] unless h[data.date]
h[data.date] << data.content
end
With a default block
h = Hash.new{|h, k| h[k] = []}
list.each do |data|
h[data.date] << data.content
end
Second line simply creates an array with two items to hold the past and future data. Since both past and the present stores the data as Hash of Hash of Array, I simply duplicate the value.
Second line can also be written as
result = [ h, h.dup]
today = Date.today

Resources