Declaring Instance Variables Iterating Over a Hash!

Declaring instance variables iterating over a hash!

class MyClass
def initialize()
hash = {"key1" => "value1","key2" => "value2","key3" => "value3"}
hash.each do |k,v|
instance_variable_set("@#{k}",v)
# if you want accessors:
eigenclass = class<<self; self; end
eigenclass.class_eval do
attr_accessor k
end
end
end
end

The eigenclass is a special class belonging just to a single object, so methods defined there will be instance methods of that object but not belong to other instances of the object's normal class.

can I pass a hash to an instance method to create instance variables?

Sure you can do something like this in your initialize method:

     hash.each do |k, v|
instance_variable_set("@#{k}", v)
self.class.send(:attr_reader, k)
end

Here's an example using your input hash:

class Reminder
def initialize(hash)
hash.each do |k, v|
instance_variable_set("@#{k}", v)
self.class.send(:attr_reader, k)
end
end
end

reminder_hash = {"bot_client_id"=>"test-client-id", "recurring"=>true, "recurring_natural_language"=>"everyday", "time_string"=>"10AM", "time_zone"=>"America/Los_Angeles", "via"=>"slack", "keyword"=>"test-keyword", "status"=>"active", "created_time"=>1444366166000}

reminder = Reminder.new(reminder_hash)
puts reminder

puts reminder.bot_client_id

Output:

#<Reminder:0x007f8a48831498>
test-client-id

Convert instance variables and their values into a hash

class Klass
def initialize
@a = 2
@b = 2
end

# define your own methods
def attributes
instance_variables.map do |var|
[var[1..-1].to_sym, instance_variable_get(var)]
end.to_h
end
end

Klass.new.attributes # => {:a=>2, :b=>2}

Convert hash params into instance variables on Ruby initializer

I want to do this in a clean way.

You won't get attr_accessors and instance variables without defining them. The below is using some simple metaprogramming (does it qualify for "clean"?)

class PriceChange
def initialize(data = {})
data.each_pair do |key, value|
instance_variable_set("@#{key}", value)
self.class.instance_eval { attr_accessor key.to_sym }
end
end
end

Usage:

price_change = PriceChange.new(foo: :foo, bar: :bar)
#=> #<PriceChange:0x007fb3a1755178 @bar=:bar, @foo=:foo>
price_change.foo
#=> :foo
price_change.foo = :baz
#=> :baz
price_change.foo
#=> :baz

Create hash from variables in loop

In your code example, the variables are all declared as being instance variables, because they're prefixed with @.

However, the variables within the loop are simply working/temporary variables, not instance variables. Additionally, x.get_some_data is probably not working, since x is just the loop variable and contains 456, abc, etc., and not an object with the desired method. Thus, the following code should produce your desired result:

def get_data(codes)
result = []
codes.each do |code|
y = get_some_data(code)
result << {
code: code,
brand: y['brand'],
size: y['size']
}
end
result
end

This is a very verbose example; you can put the whole logic in map, if the return value of get_some_data permits it.

A more elegant version would utilize Enumerable#each_with_object (the Array class includes Enumereable):

def get_data(codes)
codes.each_with_object([]) do |code, result|
y = get_some_data(code)
result << {
code: code,
brand: y['brand'],
size: y['size']
}
end
end

Thanks, Cary Swoveland, for pointing this out!

How to dynmically set instance variable through a loop?

There are several ways, how to do this:

The first one is using ruby methods: instance_variable_set and instance_variable_get:

# set new instance variable
instance_variable_set("@client_token_#{plan.id}", gateway.client_token.generate)

# read the instance variable
instance_variable_get("@client_token_#{plan.id}")

You could also use hash, instead of instance variables and it would probably be better

client_tokens = {}
@plans.map do |plan|
client_tokens[plan.id] = gateway.client_token.generate
end

# and than read it with
client_tokens[plan.id]

If you want it accessible from more places than directly in the view, you can define it as the instance variable @client_tokens = {}


If you would like to change it to completely JS thing. you could do something like this:

<% @plans.map do |plan| %>
<button onclick='braintreeClick("<%= gateway.client_token.generate %>")'... > Click </button>
...
<% end %>
<script>
function braintreeClick(client_token) {
...
}
</script>

You would generate token and put it directly to braintreeClick function call, now you don't have to bother with instance variables or more ruby code at all. This is better solution than the previous two.


The best solution would probably be to use unobtrusive javascript call. I don't know what JS framework you use, so I will demonstrate it with the jQuery:

<% @plans.map do |plan| %>
<button class="js-brain-tree-button" data-client-token="<%= gateway.client_token.generate %>" ... > Click </button>
...
<% end %>
<script>
function braintreeClick(e) {
var $button = $(e.currentTarget);
var client_token = $button.data("client-token");
...
};

$(document).ready(function() {
$(".js-brain-tree-button").on('click', braintreeClick);
});
</script>

Note: sorry if there are some typos or errors, I didn't test the code, but it should show the concept

Better way to convert several instance variables into hash with ruby?

Rails has a method on Object called instance_values for just this. Here's the code on GitHub.

class C
def initialize(x, y)
@x, @y = x, y
end
end

C.new(0, 1).instance_values # => {"x" => 0, "y" => 1}
C.new(0, 1).instance_values.symbolize_keys # => {:x => 0, :y => 1}

How to iterate over dict keys when assigning values to multiple variables

tree[hash][i] for i in "xyz" is already a generator comprehension, but it needs to be parenthesized unless it's passed as sole argument of a function like this:

my_function(tree[hash][i] for i in "xyz")   # this is valid
my_function(tree[hash][i] for i in "xyz", 12) # this is invalid, parsing conflict
my_function((tree[hash][i] for i in "xyz"), 12) # this is valid

This is the same with your assignment expression. The parentheses are needed to avoid ambiguity when parsing.

this answer solves the issue, but is creating an unnecessary list when a generator comprehension is enough:

A, B, C = (tree[hash][i] for i in "xyz")

Unpacking assigns left hand variables by iterating on the generator comprehension without creating an unnecessary list.

Also maybe avoid hashing 3 times, use a variable

h = tree[hash]
A, B, C = (h[i] for i in "xyz")

variant with map and operator.itemgetter to avoid loops:

import operator
A,B,C = map(operator.itemgetter(tree[hash]),"xyz")

Aside: avoid hash as a variable name, since it's the built-in function to hash objects.



Related Topics



Leave a reply



Submit