Using Variables For Input - ruby

I'm currently using this code to make presidential projections:
require 'csv'
require 'statistics2'
require 'pollster'
require 'uri'
include Pollster
poll = Poll.where(:chart => '2016-national-gop-primary').first
responses = poll.questions.detect { |question| question.chart == '2016-national-gop-primary' }.responses
rubio = responses.detect { |response| response[:choice] == "Rubio" }
bush = responses.detect { |response| response[:choice] == "Bush" }
walker = responses.detect { |response| response[:choice] == "Walker" }
carson = responses.detect { |response| response[:choice] == "Carson" }
huckabee = responses.detect { |response| response[:choice] == "Huckabee" }
paul = responses.detect { |response| response[:choice] == "Paul" }
cruz = responses.detect { |response| response[:choice] == "Cruz" }
difference = rubio[:value] - walker[:value]
negativechance = Statistics2.tdist(2,(difference/3))
nchance = 1 - (Statistics2.tdist(2,(difference/3)))
if difference < 0
then puts "#{nchance.to_f*100}""%" + " Walker."
puts "#{(1-nchance.to_f)*100}""%" + " Rubio."
puts "Based on latest poll at Pollster.com"
else puts "#{negativechance.to_f*100}}""%" "Rubio."
puts "#{(1-negativechance.to_f)*100}""%" + " Walker."
puts "Based on latest poll at Pollster.com"
end
I want the user to be able to input two last names to find the projection for who will win. If I change this:
difference = rubio[:value] - walker[:value]
to variables it tells me I don't have a value.
Thanks in advance.

That looks an awful lot like a KV pair (Hash), so what if you make choice the key and value the value? Using that key you can get dynamic choices very easily, but you're going to have to do some legwork here yourself.
You need to go from this:
[{a: 'foo', b: 1}, {a: 'bar', b: 21}, {a: 'baz', b: 13}]
To this:
{'foo' => 1, 'bar' => 21, 'baz' => 13}
Hint: You're going to need map and to_h
[[:a, 1]].to_h # => {a: 1}

Related

Convert Hash to OpenStruct recursively

Given I have this hash:
h = { a: 'a', b: 'b', c: { d: 'd', e: 'e'} }
And I convert to OpenStruct:
o = OpenStruct.new(h)
=> #<OpenStruct a="a", b="b", c={:d=>"d", :e=>"e"}>
o.a
=> "a"
o.b
=> "b"
o.c
=> {:d=>"d", :e=>"e"}
2.1.2 :006 > o.c.d
NoMethodError: undefined method `d' for {:d=>"d", :e=>"e"}:Hash
I want all the nested keys to be methods as well. So I can access d as such:
o.c.d
=> "d"
How can I achieve this?
You can monkey-patch the Hash class
class Hash
def to_o
JSON.parse to_json, object_class: OpenStruct
end
end
then you can say
h = { a: 'a', b: 'b', c: { d: 'd', e: 'e'} }
o = h.to_o
o.c.d # => 'd'
See Convert a complex nested hash to an object.
I came up with this solution:
h = { a: 'a', b: 'b', c: { d: 'd', e: 'e'} }
json = h.to_json
=> "{\"a\":\"a\",\"b\":\"b\",\"c\":{\"d\":\"d\",\"e\":\"e\"}}"
object = JSON.parse(json, object_class:OpenStruct)
object.c.d
=> "d"
So for this to work, I had to do an extra step: convert it to json.
personally I use the recursive-open-struct gem - it's then as simple as RecursiveOpenStruct.new(<nested_hash>)
But for the sake of recursion practice, I'll show you a fresh solution:
require 'ostruct'
def to_recursive_ostruct(hash)
result = hash.each_with_object({}) do |(key, val), memo|
memo[key] = val.is_a?(Hash) ? to_recursive_ostruct(val) : val
end
OpenStruct.new(result)
end
puts to_recursive_ostruct(a: { b: 1}).a.b
# => 1
edit
Weihang Jian showed a slight improvement to this here https://stackoverflow.com/a/69311716/2981429
def to_recursive_ostruct(hash)
hash.each_with_object(OpenStruct.new) do |(key, val), memo|
memo[key] = val.is_a?(Hash) ? to_recursive_ostruct(val) : val
end
end
Also see https://stackoverflow.com/a/63264908/2981429 which shows how to handle arrays
note
the reason this is better than the JSON-based solutions is because you can lose some data when you convert to JSON. For example if you convert a Time object to JSON and then parse it, it will be a string. There are many other examples of this:
class Foo; end
JSON.parse({obj: Foo.new}.to_json)["obj"]
# => "#<Foo:0x00007fc8720198b0>"
yeah ... not super useful. You've completely lost your reference to the actual instance.
Here's a recursive solution that avoids converting the hash to json:
def to_o(obj)
if obj.is_a?(Hash)
return OpenStruct.new(obj.map{ |key, val| [ key, to_o(val) ] }.to_h)
elsif obj.is_a?(Array)
return obj.map{ |o| to_o(o) }
else # Assumed to be a primitive value
return obj
end
end
My solution is cleaner and faster than #max-pleaner's.
I don't actually know why but I don't instance extra Hash objects:
def dot_access(hash)
hash.each_with_object(OpenStruct.new) do |(key, value), struct|
struct[key] = value.is_a?(Hash) ? dot_access(value) : value
end
end
Here is the benchmark for you reference:
require 'ostruct'
def dot_access(hash)
hash.each_with_object(OpenStruct.new) do |(key, value), struct|
struct[key] = value.is_a?(Hash) ? dot_access(value) : value
end
end
def to_recursive_ostruct(hash)
result = hash.each_with_object({}) do |(key, val), memo|
memo[key] = val.is_a?(Hash) ? to_recursive_ostruct(val) : val
end
OpenStruct.new(result)
end
require 'benchmark/ips'
Benchmark.ips do |x|
hash = { a: 1, b: 2, c: { d: 3 } }
x.report('dot_access') { dot_access(hash) }
x.report('to_recursive_ostruct') { to_recursive_ostruct(hash) }
end
Warming up --------------------------------------
dot_access 4.843k i/100ms
to_recursive_ostruct 5.218k i/100ms
Calculating -------------------------------------
dot_access 51.976k (± 5.0%) i/s - 261.522k in 5.044482s
to_recursive_ostruct 50.122k (± 4.6%) i/s - 250.464k in 5.008116s
My solution, based on max pleaner's answer and similar to Xavi's answer:
require 'ostruct'
def initialize_open_struct_deeply(value)
case value
when Hash
OpenStruct.new(value.transform_values { |hash_value| send __method__, hash_value })
when Array
value.map { |element| send __method__, element }
else
value
end
end
Here is one way to override the initializer so you can do OpenStruct.new({ a: "b", c: { d: "e", f: ["g", "h", "i"] }}).
Further, this class is included when you require 'json', so be sure to do this patch after the require.
class OpenStruct
def initialize(hash = nil)
#table = {}
if hash
hash.each_pair do |k, v|
self[k] = v.is_a?(Hash) ? OpenStruct.new(v) : v
end
end
end
def keys
#table.keys.map{|k| k.to_s}
end
end
Basing a conversion on OpenStruct works fine until it doesn't. For instance, none of the other answers here properly handle these simple hashes:
people = { person1: { display: { first: 'John' } } }
creds = { oauth: { trust: true }, basic: { trust: false } }
The method below works with those hashes, modifying the input hash rather than returning a new object.
def add_indifferent_access!(hash)
hash.each_pair do |k, v|
hash.instance_variable_set("##{k}", v.tap { |v| send(__method__, v) if v.is_a?(Hash) } )
hash.define_singleton_method(k, proc { hash.instance_variable_get("##{k}") } )
end
end
then
add_indifferent_access!(people)
people.person1.display.first # => 'John'
Or if your context calls for a more inline call structure:
creds.yield_self(&method(:add_indifferent_access!)).oauth.trust # => true
Alternatively, you could mix it in:
module HashExtension
def very_indifferent_access!
each_pair do |k, v|
instance_variable_set("##{k}", v.tap { |v| v.extend(HashExtension) && v.send(__method__) if v.is_a?(Hash) } )
define_singleton_method(k, proc { self.instance_variable_get("##{k}") } )
end
end
end
and apply to individual hashes:
favs = { song1: { title: 'John and Marsha', author: 'Stan Freberg' } }
favs.extend(HashExtension).very_indifferent_access!
favs.song1.title
Here is a variation for monkey-patching Hash, should you opt to do so:
class Hash
def with_very_indifferent_access!
each_pair do |k, v|
instance_variable_set("##{k}", v.tap { |v| v.send(__method__) if v.is_a?(Hash) } )
define_singleton_method(k, proc { instance_variable_get("##{k}") } )
end
end
end
# Note the omission of "v.extend(HashExtension)" vs. the mix-in variation.
Comments to other answers expressed a desire to retain class types. This solution accommodates that.
people = { person1: { created_at: Time.now } }
people.with_very_indifferent_access!
people.person1.created_at.class # => Time
Whatever solution you choose, I recommend testing with this hash:
people = { person1: { display: { first: 'John' } }, person2: { display: { last: 'Jingleheimer' } } }
If you are ok with monkey-patching the Hash class, you can do:
require 'ostruct'
module Structurizable
def each_pair(&block)
each do |k, v|
v = OpenStruct.new(v) if v.is_a? Hash
yield k, v
end
end
end
Hash.prepend Structurizable
people = { person1: { display: { first: 'John' } }, person2: { display: { last: 'Jingleheimer' } } }
puts OpenStruct.new(people).person1.display.first
Ideally, instead of pretending this, we should be able to use a Refinement, but for some reason I can't understand it didn't worked for the each_pair method (also, unfortunately Refinements are still pretty limited)

How can I shrink and make a better use of this method? - RUBY (raw)

Working on a project and trying to turn this method (I Have some more similar methods like that in my project) into a more dynamic and concise way
Data from image
def proficiency_parser(stored_data, name, race, year, title, percentage)
if stored_data.has_key?(name)
if stored_data[name].has_key?(race)
if stored_data[name][race].has_key?(year)
stored_data[name][race][year][title] = percentage
else
stored_data[name][race][year] = {title => percentage}
end
else
stored_data[name][race] = {year => {title => percentage}}
end
else
stored_data[name] = {race => {year => {title => percentage}}}
end
end
so essentially this method through my data to identify whether it meets so of those specification showing in the code, essentially I just don't want to use this amount of "elses" and "Ifs" if at all possible.
Data
stored_data
# => {"COLORADO"=>{3=>{2008=>{:math=>0.697}}}}
name
# => "COLORADO"
race
# => 3
year
# => 2008
title
# => :math
percentage
# => 0.697
Take a look at Hash#dig which is included in Ruby versions 2.3.0 or newer.
To summarize:
hash_1 = { a: { a: { a: "b" } } }
hash_2 = { c: { c: { c: "d" } } }
hash_1.dig(:a, :a, :a) # returns "b"
hash_2.dig(:a, :a, :a) # returns nil
So you could say if hash_1.dig(:a, :a) instead of
if hash_1[:a]
if hash_1[:a][:a]
# etc
There's also another way to do it, which is to rescue your NoMethod [] errors.
Here's an example of that:
if hash_1[:a][:a][:a] rescue false
puts "the key exists"
else
puts "the key doesnt exist"
end
You can use some recursive call
Input data
stored_data = {}
name = 'COLORADO'
race = 3
year = 2008
title = :math
percentage = 0.697
Methods
def proficiency_parser(stored_data, name, race, year, title, percentage)
parser(stored_data, name, {race => {year => {title => percentage}}})
end
def parser(data, key, value)
data[key] ? value.each { |k, v| parser(data[key], k, v) } : data[key] = value
end
call
proficiency_parser(stored_data, name, race, year, title, percentage)
p stored_data
# => {"COLORADO"=>{3=>{2008=>{:math=>0.697}}}}
I hope this helps

Array with hash, how to merge same keys and add its value

I have an array with hashes in it. If they have the same key I just want to add its value.
#receivers << result
#receivers
=> [{:email=>"user_02#yorlook.com", :amount=>10.00}]
result
=> {:email=>"user_02#yorlook.com", :amount=>7.00}
I want the result of above to look like this
[{:email=>"user_02#yorlook.com", :amount=>17.00}]
Does anyone know how to do this?
Here is the the entire method
def receivers
#receivers = []
orders.each do |order|
product_email = order.product.user.paypal_email
outfit_email = order.outfit_user.paypal_email
if order.user_owns_outfit?
result = { email: product_email, amount: amount(order.total_price) }
else
result = { email: product_email, amount: amount(order.total_price, 0.9),
email: outfit_email, amount: amount(order.total_price, 0.1) }
end
#receivers << result
end
end
Using Enumerable#group_by
#receivers.group_by {|h| h[:email]}.map do |k, v|
{email: k, amount: v.inject(0){|s,h| s + h[:amount] } }
end
# => [{:email=>"user_02#yorlook.com", :amount=>17.0}]
Using Enumerable#each_with_object
#receivers.each_with_object(Hash.new(0)) {|h, nh| nh[h[:email]]+= h[:amount] }.map do |k, v|
{email: k, amount: v}
end
# Output: [{ "em#il.one" => 29.0 }, { "em#il.two" => 39.0 }]
def receivers
return #receivers if #receivers
# Produces: { "em#il.one" => 29.0, "em#il.two" => 39.0 }
partial_result = orders.reduce Hash.new(0.00) do |result, order|
product_email = order.product.user.paypal_email
outfit_email = order.outfit_user.paypal_email
if order.user_owns_outfit?
result[product_email] += amount(order.total_price)
else
result[product_email] += amount(order.total_price, .9)
result[outfit_email] += amount(order.total_price, .1)
end
result
end
#receivers = partial_result.reduce [] do |result, (email, amount)|
result << { email => amount }
end
end
I would just write the code this way:
def add(destination, source)
if destination.nil?
return nil
end
if source.class == Hash
source = [source]
end
for item in source
target = destination.find {|d| d[:email] == item[:email]}
if target.nil?
destination << item
else
target[:amount] += item[:amount]
end
end
destination
end
usage:
#receivers = []
add(#receivers, {:email=>"user_02#yorlook.com", :amount=>10.00})
=> [{:email=>"user_02#yorlook.com", :amount=>10.0}]
add(#receivers, #receivers)
=> [{:email=>"user_02#yorlook.com", :amount=>20.0}]
a = [
{:email=>"user_02#yorlook.com", :amount=>10.0},
{:email=>"user_02#yorlook.com", :amount=>7.0}
]
a.group_by { |v| v.delete :email } # group by emails
.map { |k, v| [k, v.inject(0) { |memo, a| memo + a[:amount] } ] } # sum amounts
.map { |e| %i|email amount|.zip e } # zip to keys
.map &:to_h # convert nested arrays to hashes
From what I understand, you could get away with just .inject:
a = [{:email=>"user_02#yorlook.com", :amount=>10.00}]
b = {:email=>"user_02#yorlook.com", :amount=>7.00}
c = {email: 'user_03#yorlook.com', amount: 10}
[a, b, c].flatten.inject({}) do |a, e|
a[e[:email]] ||= 0
a[e[:email]] += e[:amount]
a
end
=> {
"user_02#yorlook.com" => 17.0,
"user_03#yorlook.com" => 10
}

Safely assign value to nested hash using Hash#dig or Lonely operator(&.)

h = {
data: {
user: {
value: "John Doe"
}
}
}
To assign value to the nested hash, we can use
h[:data][:user][:value] = "Bob"
However if any part in the middle is missing, it will cause error.
Something like
h.dig(:data, :user, :value) = "Bob"
won't work, since there's no Hash#dig= available yet.
To safely assign value, we can do
h.dig(:data, :user)&.[]=(:value, "Bob") # or equivalently
h.dig(:data, :user)&.store(:value, "Bob")
But is there better way to do that?
It's not without its caveats (and doesn't work if you're receiving the hash from elsewhere), but a common solution is this:
hash = Hash.new {|h,k| h[k] = h.class.new(&h.default_proc) }
hash[:data][:user][:value] = "Bob"
p hash
# => { :data => { :user => { :value => "Bob" } } }
And building on #rellampec's answer, ones that does not throw errors:
def dig_set(obj, keys, value)
key = keys.first
if keys.length == 1
obj[key] = value
else
obj[key] = {} unless obj[key]
dig_set(obj[key], keys.slice(1..-1), value)
end
end
obj = {d: 'hey'}
dig_set(obj, [:a, :b, :c], 'val')
obj #=> {d: 'hey', a: {b: {c: 'val'}}}
interesting one:
def dig_set(obj, keys, value)
if keys.length == 1
obj[keys.first] = value
else
dig_set(obj[keys.first], keys.slice(1..-1), value)
end
end
will raise an exception anyways if there's no [] or []= methods.
I found a simple solution to set the value of a nested hash, even if a parent key is missing, even if the hash already exists. Given:
x = { gojira: { guitar: { joe: 'charvel' } } }
Suppose you wanted to include mario's drum to result in:
x = { gojira: { guitar: { joe: 'charvel' }, drum: { mario: 'tama' } } }
I ended up monkey-patching Hash:
class Hash
# ensures nested hash from keys, and sets final key to value
# keys: Array of Symbol|String
# value: any
def nested_set(keys, value)
raise "DEBUG: nested_set keys must be an Array" unless keys.is_a?(Array)
final_key = keys.pop
return unless valid_key?(final_key)
position = self
for key in keys
return unless valid_key?(key)
position[key] = {} unless position[key].is_a?(Hash)
position = position[key]
end
position[final_key] = value
end
private
# returns true if key is valid
def valid_key?(key)
return true if key.is_a?(Symbol) || key.is_a?(String)
raise "DEBUG: nested_set invalid key: #{key} (#{key.class})"
end
end
usage:
x.nested_set([:instrument, :drum, :mario], 'tama')
usage for your example:
h.nested_set([:data, :user, :value], 'Bob')
any caveats i missed? any better way to write the code without sacrificing readability?
Searching for an answer to a similar question I developmentally stumbled upon an interface similar to #niels-kristian's answer, but wanted to also support a namespace definition parameter, like an xpath.
def deep_merge(memo, source)
# From: http://www.ruby-forum.com/topic/142809
# Author: Stefan Rusterholz
merger = proc { |key, v1, v2| Hash === v1 && Hash === v2 ? v1.merge(v2, &merger) : v2 }
memo.merge!(source, &merger)
end
# Like Hash#dig, but for setting a value at an xpath
def bury(memo, xpath, value, delimiter=%r{\.})
xpath = xpath.split(delimiter) if xpath.respond_to?(:split)
xpath.map!{|x|x.to_s.to_sym}.push(value)
deep_merge(memo, xpath.reverse.inject { |memo, field| {field.to_sym => memo} })
end
Nested hashes are sort of like xpaths, and the opposite of dig is bury.
irb(main):014:0> memo = {:test=>"value"}
=> {:test=>"value"}
irb(main):015:0> bury(memo, 'test.this.long.path', 'value')
=> {:test=>{:this=>{:long=>{:path=>"value"}}}}
irb(main):016:0> bury(memo, [:test, 'this', 2, 4.0], 'value')
=> {:test=>{:this=>{:long=>{:path=>"value"}, :"2"=>{:"4.0"=>"value"}}}}
irb(main):017:0> bury(memo, 'test.this.long.path.even.longer', 'value')
=> {:test=>{:this=>{:long=>{:path=>{:even=>{:longer=>"value"}}}, :"2"=>{:"4.0"=>"value"}}}}
irb(main):018:0> bury(memo, 'test.this.long.other.even.longer', 'other')
=> {:test=>{:this=>{:long=>{:path=>{:even=>{:longer=>"value"}}, :other=>{:even=>{:longer=>"other"}}}, :"2"=>{:"4.0"=>"value"}}}}
A more ruby-helper-like version of #niels-kristian answer
You can use it like:
a = {}
a.bury!([:a, :b], "foo")
a # => {:a => { :b => "foo" }}
class Hash
def bury!(keys, value)
key = keys.first
if keys.length == 1
self[key] = value
else
self[key] = {} unless self[key]
self[key].bury!(keys.slice(1..-1), value)
end
self
end
end

Building a hash out of hash

I'm trying to write my code more compact. I have three hashes. The first hash (#hash) is a collection of sub-hashes (value_1, value_2)
#hash = {
"Key" => ["value_1", "value_2"]
}
#value_1 = {
"Foo" => ["bar_1", "bar_2"]
}
#value_2 = {
"Foo2" => ["bar2_1", "bar2_2"]
}
Now, in my haml-view i'm trying to make something like this:
- i = 0
- #hash.each_value do |value|
- #value_[i].each_pair do |k, v|
= k
= v[0]
- i = i +1
I don't want to write one hash after the other. It's a bit similar to making a symbol out of a string, where you can write somthing like "value_#{i}".to_sym. I hope, somebody can follow and help me.
#hashes = [
{
"Foo" => ["bar_1", "bar_2"]
},
{
"Foo2" => ["bar2_1", "bar2_2"]
}
]
Then
#hashes.each do |v|
v.each_pair do |k,v|
= k
= v[0]
And if you need the index use each_with_index.
EDIT
Try this:
- #hash.each_value do |value|
- value.each do |v|
- instance_variable_get(:"##{v}").each_pair do |k, val|
= k
= val[0]
API

Resources