How to set a variable to another instance by reference - ruby-on-rails

given the following code:
vars = instance_variables.map(&method(:instance_variable_get))
vars.each {|v| v = 123}
would that set #something = 123?
taking it a step further
is it the same if i have
vars = instance_variables.map(&method(:instance_variable_get))
vars.each {|v| doSomething(v) }
def doSomething(var)
var = 123
end
how would i mutate var from inside a function?

You can test this in irb pretty quickly:
#something = 456
instance_variables
# => [:#something]
instance_variables.map(&method(:instance_variable_get)).each { |v| v = 123 }
#something
# => 456 (i.e. "didn't mutate #something")
def doSomething(var)
var = 123
end
vars = instance_variables.map(&method(:instance_variable_get))
vars.each { |v| doSomething(v) }
#something
# => 456 (i.e. "didn't mutate #something")
Object#instance_variable_set, however, does change the value of #something:
#something = 456
instance_variables.each { |v| instance_variable_set(v, 123) }
#something
# => 123

Agreeing with pje, you probably should have tested this in irb, but I'm assuming you want to capture a setter for every instance variable, so I'd recommend something like:
setters = instance_variables.map{|v| lambda { |val| instance_variable_set(v, val) }}
then you can just do setters[0].call(__VALUE__) and it will set the value accordingly.
What is it you are trying to achieve?

Related

Return members of a hashmap through a class get method

The following returns the default "client?":
class ClientMap
def initialize
##clients = {"DP000459": "BP"}
##clients.default = "client?"
end
def get(id)
return ##clients[:id]
end
end
clientMap = ClientMap.new
cKey = "DP000459"
puts clientMap.get(cKey)
Could anybody explain why I cannot retrieve anything but the 'default'?
You've got two problems. First, you are using the symbol syntax in your hash, which works only if your keys are symbols. If you want keys to be strings, you need to use hash-rocket syntax: ##clients = {'DP000459' => 'BP'}.
Second, your method returns clients[:id] regardless of what parameter is provided. The key is the symbol :id rather than the local variable id. You need to change this to ##clients[id].
Here's a cleaned-up version of what you want:
class ClientMap
def initialize
##clients = {'DP000459' => 'BP'}
##clients.default = 'client?'
end
def get(id)
##clients[id]
end
end
I've also taken the liberty of making the spacing more Ruby-idiomatic.
Finally, for variable names in Ruby, use snake_case:
>> client_map = ClientMap.new
>> c_key = 'DP000459'
>> client_map.get(c_key)
#> "BP"
Look at these code:
h = { foo: 'bar' } # => {:foo=>"bar"}
h.default = 'some default value' # => "some default value"
h[:foo] # => "bar"
h[:non_existing_key] # => "some default value"
You can read here about Hash#default method
Returns the default value, the value that would be returned by hsh if
key did not exist in hsh

Is there a method to set a value in rails to nil and save?

What I'm thinking of is something where I can say:
e = Foo.new
e.bar = "hello"
e.save
e.reload
e.bar.nil!
e.reload
e.bar.nil? => true
Kind of #touch but sets nil and saves.
EDIT
Super sorry guys. I mean this:
e = Foo.new
e.bar = "hello"
e.save
e.reload
e.bar.nil!
e.reload
e.bar.nil? => true
Maybe something like:
module ActiveRecord
class Base
def nil!(*names)
unless persisted?
raise ActiveRecordError, <<-MSG.squish
cannot nil on a new or destroyed record object. Consider using
persisted?, new_record?, or destroyed? before nilling
MSG
end
unless names.empty?
changes = {}
names.each do |column|
column = column.to_s
changes[column] = write_attribute(column, nil)
end
primary_key = self.class.primary_key
scope = self.class.unscoped.where(primary_key => _read_attribute(primary_key))
if locking_enabled?
locking_column = self.class.locking_column
scope = scope.where(locking_column => _read_attribute(locking_column))
changes[locking_column] = increment_lock
end
clear_attribute_changes(changes.keys)
result = scope.update_all(changes) == 1
if !result && locking_enabled?
raise ActiveRecord::StaleObjectError.new(self, "nil")
end
#_trigger_update_callback = result
result
else
true
end
end
end
end
Put that in an initializer and it'll let you null out the title of a comment with Comment.last.nil!(:title).
You can't save a nil to the database, and furthermore, once an object has been created as a particular class you can never change that. It can only be converted by creating a new object, something an in-place modifier like this hypothetical nil! does.
The closest thing you can get is:
e = Foo.new
e.bar = "hello"
e.save
e.reload
e.delete!
e.reload
e.destroyed? # => true
f = Foo.find_by(id: e.id)
f.nil? # => true

dynamically create a hash in ruby

I am sure this is a question that has been asked before, but my research still hasn't led me to any conclusive answer. I am trying to create a standard key value pair in a loop.
The code below iterates as expected, but it is only storing the last value. I understand why, because I am overwriting the delivery_hash variable each time by use of the =. I tried using the shovel operator that I have used for arrays, but that didn't work.
def calculate_job_delivery_costs
delivery_hash = {}
DELIVERY_COST_SCOPES.each do |scope|
delivery_hash = { scope => job_delivery_costs.send(scope).inject(0) { |total, item| (total + item.cost_per_unit * item.hour_count) * item.quantity } }
end
delivery_hash
end
my desired output is simply like this
"foo" => 234.32, 'bah' => 345.76, 'baz' => 33.87
Well, one thing is just stop blowing away the variable, and use the hash you created in the first place:
def calculate_job_delivery_costs
delivery_hash = {}
DELIVERY_COST_SCOPES.each do |scope|
delivery_hash[scope] = job_delivery_costs.send(scope).inject(0) do |total, item|
(total + item.cost_per_unit * item.hour_count) * item.quantity
end
end
delivery_hash
end
Or, simpler, just build the hash from the individual results:
def calculate_job_delivery_costs
delivery_hashes = DELIVERY_COST_SCOPES.map do |scope|
cost = job_delivery_costs.send(scope).inject(0) do |total, item|
(total + item.cost_per_unit * item.hour_count) * item.quantity
end
[scope, cost]
end
Hash[delivery_hashes]
end
try with merge
h1 = { "a" => 100, "b" => 200 }
h2 = { "b" => 254, "c" => 300 }
h1.merge(h2) #=> {"a"=>100, "b"=>254, "c"=>300}
according to the ruby documentation: http://www.ruby-doc.org/core-2.1.2/Hash.html#method-i-merge

constructing a new hash from the given values

I seem lost trying to achieve the following, I tried all day please help
I HAVE
h = {
"kv1001"=> {
"impressions"=>{"b"=>0.245, "a"=>0.754},
"visitors" =>{"b"=>0.288, "a"=>0.711},
"ctr" =>{"b"=>0.003, "a"=>0.003},
"inScreen"=>{"b"=>3.95, "a"=>5.031}
},
"kv1002"=> {
"impressions"=>{"c"=>0.930, "d"=>0.035, "a"=>0.004, "b"=>0.019,"e"=>0.010},
"visitors"=>{"c"=>0.905, "d"=>0.048, "a"=>0.005, "b"=>0.026, "e"=>0.013},
"ctr"=>{"c"=>0.003, "d"=>0.006, "a"=>0.004, "b"=>0.003, "e"=>0.005},
"inScreen"=>{"c"=>4.731, "d"=>4.691, "a"=>5.533, "b"=>6.025, "e"=>5.546}
}
}
MY GOAL
{
"segment"=>"kv1001=a",
"impressions"=>"0.754",
"visitors"=>"0.711",
"inScreen"=>"5.031",
"ctr"=>"0.003"
}, {
"segment"=>"kv1001=b",
"impressions"=>"0.245",
"visitors"=>"0.288",
"inScreen"=>"3.95",
"ctr"=>"0.003"
}, {
"segment"=>"kv1002=a",
"impressions"=>"0.004"
#... etc
}
My goal is to create a hash with 'kv1001=a' i.e the letters inside the hash and assign the keys like impressions, visitors etc. The example MY GOAL has the format
So format type "kv1001=a" must be constructed from the hash itself, a is the letter inside the hash.
I have solved this now
`data_final = []
h.each do |group,val|
a = Array.new(26){{}}
val.values.each_with_index do |v, i|
keys = val.keys
segment_count = v.keys.length
(0..segment_count-1).each do |n|
a0 = {"segment" => "#{group}=#{v.to_a[n][0]}", keys[i] => v.to_a[n][1]}
a[n].merge! a0
if a[n].count > 4
data_final << a[n]
end
end
end
end`
Here's a simpler version
h.flat_map do |segment, attrs|
letters = attrs.values.flat_map(&:keys).uniq
# create a segment entry for each unique letter
letters.map do |letter|
seg = {"segment" => "#{segment}=#{letter}"}
seg.merge Hash[attrs.keys.map {|key| [key,attrs[key][letter]]}]
end
end
Output:
[{"segment"=>"kv1001=b",
"impressions"=>0.245,
"visitors"=>0.288,
"ctr"=>0.003,
"inScreen"=>3.95},
{"segment"=>"kv1001=a",
"impressions"=>0.754,
"visitors"=>0.711,
"ctr"=>0.003,
"inScreen"=>5.031},
{"segment"=>"kv1002=c",
"impressions"=>0.93,
"visitors"=>0.905,
"ctr"=>0.003,
"inScreen"=>4.731},
{"segment"=>"kv1002=d",
"impressions"=>0.035,
"visitors"=>0.048,
"ctr"=>0.006,
"inScreen"=>4.691},
{"segment"=>"kv1002=a",
"impressions"=>0.004,
"visitors"=>0.005,
"ctr"=>0.004,
"inScreen"=>5.533},
{"segment"=>"kv1002=b",
"impressions"=>0.019,
"visitors"=>0.026,
"ctr"=>0.003,
"inScreen"=>6.025},
{"segment"=>"kv1002=e",
"impressions"=>0.01,
"visitors"=>0.013,
"ctr"=>0.005,
"inScreen"=>5.546}]

Call a generic function with or without parameters

I had a code looking like this:
def my_function(obj)
if obj.type == 'a'
return [:something]
elsif obj.type == 'b'
return []
elsif obj.type == 'c'
return [obj]
elsif obj.type == 'd'
return [obj]*2
end
end
I want to separate all these if...elsif blocks into functions like this:
def my_function_with_a
return [:something]
end
def my_function_with_b
return []
end
def my_function_with_c(a_parameter)
return [a_parameter]
end
def my_function_with_d(a_parameter)
return [a_parameter] * 2
end
I call these functions with
def my_function(obj)
send(:"my_function_with_#{obj.type}", obj)
end
The problem is that some functions need parameters, others do not. I can easily define def my_function_with_a(nothing=nil), but I'm sure there is a better solution to do this.
#Dogbert had a great idea with arity. I have a solution like this:
def my_function(obj)
my_method = self.method("my_function_with_#{obj.type}")
return (method.arity.zero? ? method.call : method.call(obj))
end
Check how to call methods in Ruby, for that I will recommend you this two resources: wikibooks and enter link description here.
Take a special note on optional arguments where you can define a method like this:
def method(*args)
end
and then you call call it like this:
method
method(arg1)
method(arg1, arg2)
def foo(*args)
[ 'foo' ].push(*args)
end
>> foo
=> [ 'foo' ]
>> foo('bar')
=> [ 'foo', 'bar' ]
>> foo('bar', 'baz')
=> [ 'foo', 'bar', 'baz' ]
def my_function(obj)
method = method("my_function_with_#{obj.type}")
method.call(*[obj].first(method.arity))
end
Change your function to something like:
def my_function_with_foo(bar=nil)
if bar
return ['foo', bar]
else
return ['foo']
end
end
Now the following will both work:
send(:"my_function_with_#{foo_bar}")
=> ['foo']
send(:"my_function_with_#{foo_bar}", "bar")
=> ['foo', 'bar']
You can also write it like this if you don't want to use if/else and you're sure you'll never need nil in the array:
def my_function_with_foo(bar=nil)
return ['foo', bar].compact
end
You can use a default value
def fun(a_param = nil)
if a_param
return ['raboof',a_param]
else
return ['raboof']
end
end
or...
def fun(a_param : nil)
if a_param
return ['raboof',a_param]
else
return ['raboof']
end
end
The latter is useful if you have multiple parameters because now when you call it you can just pass in the ones that matter right now.
fun(a_param:"Hooray")

Resources