Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 3 years ago.
Improve this question
For example I have this data:
headings = {
:heading1 => { :weight => 60, :show_count => 0}
:heading2 => { :weight => 10, :show_count => 0}
:heading3 => { :weight => 20, :show_count => 0}
:heading4 => { :weight => 10, :show_count => 0}
}
total_views = 0
Now I want to serve each heading based on their weightages. For instance, for first 10 requests/iterations, heading1, heading3, heading2 and heading4 would be served 6, 2, 1, and 1 times respectively in order (by weight).
For every iteration show_count of served heading will increment by one and total_views will also increment globally.
Could you please suggest an algorithm or some ruby code to handle this.
You can use pickup gem
It accepts hash like this:
require 'pickup'
headings = {
heading1: 60,
heading2: 10,
heading3: 20,
heading4: 10
}
pickup = Pickup.new(headings)
pickup.pick
#=> heading1
pickup.pick
#=> heading1
pickup.pick
#=> heading3
pickup.pick
#=> heading1
pickup.pick
#=> heading4
So you can do something like this:
require 'pickup'
headings = {
heading1: { :weight => 60, :show_count => 0},
heading2: { :weight => 10, :show_count => 0},
heading3: { :weight => 20, :show_count => 0},
heading4: { :weight => 10, :show_count => 0}
}
pickup_headings = headings.inject({}){ |h, (k,v)| h[k] = v[:weight]; h}
pickup = Pickup.new(pickup_headings)
# let's fire it 1000 times
1000.times do
server = pickup.pick
headings[server][:show_count] += 1
end
puts headings
#=> {
#=> :heading1=>{:weight=>60, :show_count=>601},
#=> :heading2=>{:weight=>10, :show_count=>116},
#=> :heading3=>{:weight=>20, :show_count=>176},
#=> :heading4=>{:weight=>10, :show_count=>107}
#=> }
This should work for your basic case and can be modified according to the details of what you need:
class Heading
attr_reader :heading, :weight, :show_count
def initialize(heading,weight=1)
#heading=heading
#weight=weight
#show_count=0
end
def serve
puts "Served #{#heading}! "
#show_count += 1
end
end
class HeadingServer
attr_reader :headings
def initialize(headings_hash)
#headings=headings_hash.map {|h, data| Heading.new(h,data[:weight])}
#total_weight=#headings.inject(0) {|s,h| s+= h.weight}
end
def serve(num_to_serve=#total_weight)
#headings.sort {|a,b| b.weight <=> a.weight}.each do |h|
n = (h.weight * num_to_serve) / #total_weight #possibility of rounding errors
n.times { h.serve }
end
end
def total_views
#headings.inject(0) {|s,h| s += h.show_count}
end
end
headings = {
:heading1 => { :weight => 60, :show_count => 0},
:heading2 => { :weight => 10, :show_count => 0},
:heading3 => { :weight => 20, :show_count => 0},
:heading4 => { :weight => 10, :show_count => 0}
}
# Example Usage:
hs = HeadingServer.new(headings)
hs.serve(10)
hs.headings.each {|h| puts "#{h.heading} : served #{h.show_count} times"}
puts "Total Views: #{hs.total_views}"
Related
I've been given the following data structure:
users = {
"Jonathan" => {
:twitter => "tronathan",
:favorite_numbers => [12, 42, 75],
},
"Erik" => {
:twitter => "sferik",
:favorite_numbers => [8, 12, 24],
},
"Anil" => {
:twitter => "bridgpal",
:favorite_numbers => [12, 14, 85],
},
}
I need to return all of Anils favourite numbers that are even.
This is what I have so far:
users["Anil"][:favorite_numbers].each do |evennum|
if evennum.even?
puts evennum
end
end
You could do something like this
anil_favorite_even_numbers = users['Anil'][:favorite_numbers].select(&:even?)
This takes for granted that a user Anil exists and the favourite_numbers inside it too and that's an array. Otherwise we need a little bit of extra work.
I've been struggling with this. I'm trying to write this blackjack game where I need to draw a random card, push it into an array, compare it against a hash which has the scores of each card and return the final score. I've written the following, however it's not drawing one card at a time.
This is the output (please note I'm putsing the array instead of the final score which helped me figure out what went wrong). The random_card is a method to generate a random card which I haven't included here.
I'm pretty sure it's something to do with my score method.
Hit or stick?
hit
Score so far: [10]
Hit or stick?
hit
Hit or stick?
hit
Score so far: [10, 10, 10]
Hit or stick?
hit
Hit or stick?
hit
Score so far: [10, 10, 10, 10, 10, 10]
This is my code
$score_so_far = []
$total_score = 0
def score(cards)
card_scores = {
"two" => 2,
"three" => 3,
"four" => 4,
"five" => 5,
"six" => 6,
"seven" => 7,
"eight" => 8,
"nine" => 9,
"ten" => 10,
"jack" => 10,
"queen" => 10,
"king" => 10,
"ace" => 11
}
cards.each do |x|
card_scores.each do |k, v|
if k === x
$score_so_far.push(v)
end
end
end
$total_score = $score_so_far.sum
end
def run_game
move = gets.chomp
$hand = []
while true do
if move == "stick"
break
elsif move == "hit"
$hand << random_card
score($hand)
puts "Score so far: #{$score_so_far}"
end
end
if $total_score <= 21
puts "You scored: #{$total_score}"
elsif $total_score > 21
puts "You busted with: #{$total_score}"
end
end
run_game
If anyone is able to offer some assistance, that would be much appreciated.
The score method mutating global variables every time you call it. So if you call it twice for the same card set you see different results.
def score(cards)
card_scores = {
"two" => 2,
"three" => 3,
"four" => 4,
"five" => 5,
"six" => 6,
"seven" => 7,
"eight" => 8,
"nine" => 9,
"ten" => 10,
"jack" => 10,
"queen" => 10,
"king" => 10,
"ace" => 11
}
score_so_far = []
cards.each do |x|
card_scores.each do |k, v|
if k === x
score_so_far.push(v)
end
end
end
score_so_far.sum
end
So instead of mutating something outside you just sum values of cards and return it.
Also you have to change run_game method not to global variables too:
# ...
current_score = 0
while true do
if move == "stick"
# ...
elsif move == "hit"
# ...
current_score = score(hand)
puts "Score so far: #{current_score}"
end
end
if current_score <= 21
puts "You scored: #{current_score}"
elsif current_score > 21
puts "You busted with: #{current_score}"
end
end
I see you changed something in the run_game method because calling it will cause an infinite loop.
I have an input as follows:
input = [
["account1",10,"Dr"],
["account1",20,"Dr"],
["account2",15,"Cr"],
["account1",25,"Cr"],
["account2",10,"Dr"],
["account1",10,"Cr"]
]
I am trying to get the sums by account and transaction type, i.e., Dr or Cr. I need an output as below:
output = {
["account1","Dr"] => 30,
["account1","Cr"] => 35,
["account2","Dr"] => 10,
["account2","Cr"] => 15
}
I can sum the amount based on only account using:
input.each_with_object(Hash.new(0)) {|(f,g), h| h[f] += g}
# => {"account1"=>65, "account2"=>25}
You can do so:
input.each_with_object(Hash.new(0)) {|(f,g,i), h| h[[f,i]] += g}
=> {["account1", "Dr"]=>30, ["account2", "Cr"]=>15, ["account1", "Cr"]=>35, ["account2", "Dr"]=>10}
input.group_by { |acc,_,title| [acc, title] }.
transform_values { |v| v.sum { |a| a[1] } }
#=> {["account1", "Dr"]=>30, ["account2", "Cr"]=>15, ["account1", "Cr"]=>35,
# ["account2", "Dr"]=>10}
The first step is the following.
input.group_by { |acc,_,title| [acc, title] }
#=> {
# ["account1", "Dr"]=>[["account1", 10, "Dr"], ["account1", 20, "Dr"]],
# ["account2", "Cr"]=>[["account2", 15, "Cr"]],
# ["account1", "Cr"]=>[["account1", 25, "Cr"], ["account1", 10, "Cr"]],
# ["account2", "Dr"]=>[["account2", 10, "Dr"]]
# }
output = Hash.new(0) # set a default value of zero, avoiding nil
input.each do |account, amount, transaction|
output[[account, transaction]] += amount
end
output # {["account1", "Dr"]=>30, ["account2", "Cr"]=>15, ["account1", "Cr"]=>35, ["account2", "Dr"]=>10}
I have an array of hashes like below:
items = [ {"id" => 1, "cost" => '2.00'},
{"id" => 2, "cost" => '6.00'},
{"id" => 1, "cost" => '2.00'},
{"id" => 1, "cost" => '2.00'},
{"id" => 1, "cost" => '2.00'} ]
I would like to update the cost to '8.00' where the id = 1. I have tried with the each method like below which does work but I would like to know if there is another more efficient way of updating the values?
items.each { |h| h["cost"] = "8.00" if h["id"] == 1 }
You could just use the same object:
item_1 = {'id' => 1, 'cost' => '2.00'}
item_2 = {'id' => 2, 'cost' => '6.00'}
items = [item_1, item_2, item_1, item_1, item_1]
#=> [{"id"=>1, "cost"=>"2.00"}, {"id"=>2, "cost"=>"6.00"},
# {"id"=>1, "cost"=>"2.00"}, {"id"=>1, "cost"=>"2.00"},
# {"id"=>1, "cost"=>"2.00"}]
This makes updates trivial:
item_1['cost'] = '8.00'
items
#=> [{"id"=>1, "cost"=>"8.00"}, {"id"=>2, "cost"=>"6.00"},
# {"id"=>1, "cost"=>"8.00"}, {"id"=>1, "cost"=>"8.00"},
# {"id"=>1, "cost"=>"8.00"}]
You might consider changing your data structure from:
items = [{"id" => 1, "cost" => '2.00'}, {"id" => 2, "cost" => '6.00'},
{"id" => 1, "cost" => '2.00'}, {"id" => 1, "cost" => '2.00'},
{"id" => 1, "cost" => '2.00'}]
To a hash like this:
items = { 1 => '2.00', 2 => '6.00' }
To updating the record with id = 1 to 8.00 call:
items[1] = '8.00'
Or if you need to know the number of items, you might want to conside a structure like this:
items = { 1 => ['2.00', 4], 2 => ['6.00', 1] }
Than update like this:
items[1][0] = '8.00'
You can achieve this by using each on array
items.each{|v| v["cost"] = "8.00" if v["id"] == 1 }
Cheers!
I have a hash like this:
document = {
"results" => {[
{"ip" => 10, "host" => 12},
{"ip" => 13, "host" => 17}
]}
}
It's a one item hash with an array of hashes inside the hash. I specified a value of ip = 10.
If the ip is more than 10, I want to print both keys with values. This hash is very complicated and I don't know how to access these values. Can you help me?
Edit:
What if I had hash like this
document = { "results" => [{"ip" => 10, "host" => 12, "temp" => yes},{"ip" => 13, "host" => 17, "temp" => yes}] } and wanted print only ip and host after matching ip with 10?
document["results"].each do |result|
if result["ip"] > 10
puts result # will print ip and host
end
end
I would use select:
document = { "results" => [{"ip" => 10, "host" => 12},{"ip" => 13, "host" => 17}] }
puts document['results'].select { |hash| hash['ip'] > 10 }
#=> {"ip"=>13, "host"=>17}
Explanation:
document['results']
returns the array of hashes:
[{"ip" => 10, "host" => 12},{"ip" => 13, "host" => 17}]
In the next step select is called on that returned array:
document['results'].select { |hash| hash['ip'] > 10 }
This returns all sub-hashes with an value > 10 assigned to the key 'ip'.
puts just prints the result to STDOUT
I have another problem today. Here is my code:
require 'rubygems'
require 'json'
document = JSON.load File.new("hosts.txt")
file = JSON.load File.new("admins.txt")
first_table = document["results"]
second_table = file["admins"]
new_one = first_table | second_table
First hash looks like this:
document = { "results" => [{"ip" => 10, "host" => 12},{"ip" => 13, "host" => 17}] }
The second hash is
file = { "admins" => [{"host" => 12, "name" => 12},{"host" => 17, "name" => 17}] }
I want merge these two hashes matching them by host by the same value to get
{ "new_one" => [{"ip" => 10, "host" => 12, "name" => 12}, {"ip" => 13, "host" => 17}, "name" => 17]
When I try new_one = first_table | second_tableit says test.rb:24:in <main>': undefined method|' for #Hash:0x00000002ca8be8 (NoMethodError) and when I try new_one = first_table.merge(second_table)its says test.rb:26:in <main>': undefined methodmerge' for #Array:0x00000002ce88b0(NoMethodError). So what is wrong with these hashes? One time they are hashes and the second time they are arrays? How to mach these hashes? The keys and values of host the same in both hashes.