dynamic variable names or using a variable as another's name

P

Peter Buckley

Hi-

I've done some searching on this topic and it seems to be generally
discouraged to use one variable's value as another variable's name (or
part of it's name). People generally recommend a hash or array to answer
this use case.

I've seen these posted as "available but not recommended":

eval("#{variable_name} = #[value}")

or:

self.instance_variable_set:)@var_name, value)

or:

x="myvar"
myvar="hi"
eval(x) -> "hi"

I'm trying to write a short program and I'm thinking that I'm going in
the wrong direction - can anyone give me some fresh ideas as an
alternative to what I see as my "best solution?"

I have string1, stringA (which won't be known till later) that is
associated with string1, and then stringB and potentially stringC and
stringD which are all associated with string1 and stringA.

So I'm thinking that I want a hash, big_hash, with string1 as the key,
and the value is another hash, string1_hash, with stringA as the key and
the value is an array containing stringB,stringC,stringD.

I realize I could name string1_hash anything, but I'm going to need
multiple hashes as I iterate over a list of string1, string2, string3.
I'd like big_hash to be somewhat permanent so I can re-use it elsewhere.

I'm not looking for someone to solve the problem for me, but really just
for other ideas beyond my clunky-semi-permanent data structure. Do I
need to store these associated values in a database, rather than trying
to build one out of these hashes? Is my direction of making big_hash
semi-permanent a bad idea, and I should just keep re-using big_hash over
and over as I iterate through my list of string1, string2, string3? As
I'm asking the question I think that is the direction I'm going to try
next, but I would still appreciate advice on this topic.

TIA,
Peter
 
M

matt neuburg

Peter Buckley said:
I have string1, stringA (which won't be known till later) that is
associated with string1, and then stringB and potentially stringC and
stringD which are all associated with string1 and stringA.

That sounds like a hash where each value is a hash where each value is
an array.
So I'm thinking that I want a hash, big_hash, with string1 as the key,
and the value is another hash, string1_hash, with stringA as the key and
the value is an array containing stringB,stringC,stringD.

Great idea!
I realize I could name string1_hash anything, but I'm going to need
multiple hashes as I iterate over a list of string1, string2, string3.
I'd like big_hash to be somewhat permanent so I can re-use it elsewhere.

There seems to be some confusion about the notion of "name" underlying
all of this. There are not really any "names" in this story. Ruby isn't
about "names". It's about pointers. string1_hash doesn't *have* a
"name". It doesn't *need* a name. It is the value of some entry in
big_hash. As long as you have a pointer to big_hash, you will always
have access to its keys and values, and every one of those values is a
hash of arrays, where the key is string1, string2, string3, whatever.
You are there.

So now the only question seems to be what you mean about the permanence
of big_hash. Do you mean you'd like to store it between runs of your
script? I'd use yaml for that. If you only mean that you need to be able
to access it in various places during a single run of the script, then
either make your pointer to it global or pass it around as a parameter.

By the way, you *could* make all this a bit more rigorous by expressing
it all as a class. I mention this only because a class might help you
formalize the rules for the structure here. You don't *need* to do that,
but it might help you catch errors down the road. m.
 
P

Peter Buckley

matt said:
There seems to be some confusion about the notion of "name" underlying
all of this. There are not really any "names" in this story. Ruby isn't
about "names". It's about pointers. string1_hash doesn't *have* a
"name". It doesn't *need* a name. It is the value of some entry in
big_hash. As long as you have a pointer to big_hash, you will always
have access to its keys and values, and every one of those values is a
hash of arrays, where the key is string1, string2, string3, whatever.
You are there.

Ok, I'm learning that everything is an object, everything is a pointer
in Ruby. But pointers though not required to have names, have names. It
seems to make for more readable code in some circumstances.

So here's my snippet: (forgive me, this is the first ruby I've tried so
it is probably weak)

bigHash = Hash.new { |h,k| h[k] = [] }

p4.run_changes("-m5","//depot/path/...").each {
|ch|
string1 = ch["change"]
bigHash[string1] = string1_hash
}
puts(bigHash.inspect)

This iterates 5 times, each time with string1 being equal to a different
string, so bigHash has 5 keys. The problem I'm having is that the value
of each of the 5 keys is the same - string1_hash.

To take an analogy from perl, I would have set $i to 0 and incremented
such that when I did:

bigHash[string1] = string${i}_hash

That would set the value for each unique string1 key to a unique hash,
each with unique a name that is meaningful to me and can be easily
referenced later.

So I guess I just don't know how to assign unique anonymous hashes as
the value for each key in bigHash? So I don't care that string1_hash
isn't named "string1_hash" - ok, makes sense - but then I guess I'm
still asking this question about using one variable's value as another's
name.

If "no name" hash is the value of bigHash[string1], I guess coming from
perl I'm not sure how to de-reference this appropriately, when I have
the feeling that I don't need to de-reference anything. I just don't
know how to correctly use the syntax to use the hash values of one hash
as the thing that they are, a pointer to another hash that I want to
populate with keys and values.

Maybe I want to do somthing like this?

bigHash = Hash.new { |h,k| h[k] = [] }

p4.run_changes("-m5","//depot/path/...").each {
|ch|
string1 = ch["change"]
bigHash[string1] = Hash.new { |h,k| h[k] = [] }
}
puts(bigHash.inspect)
 
M

matt neuburg

Peter Buckley said:
bigHash = Hash.new { |h,k| h[k] = [] }

p4.run_changes("-m5","//depot/path/...").each {
|ch|
string1 = ch["change"]
bigHash[string1] = string1_hash
}
puts(bigHash.inspect)

This iterates 5 times, each time with string1 being equal to a different
string, so bigHash has 5 keys. The problem I'm having is that the value
of each of the 5 keys is the same - string1_hash.

Because you *set* them all to string_hash, in this line:
bigHash[string1] = string1_hash

What were you *trying* to do?

Also, don't you have this upside down? I thought we agreed you were
going to have *one* key called string1, whose value was an array, and
you were going to put 5 different things into that array. I don't see
you doing any of that.

m.
 
P

Peter Buckley

matt said:
Peter Buckley said:
string, so bigHash has 5 keys. The problem I'm having is that the value
of each of the 5 keys is the same - string1_hash.

Because you *set* them all to string_hash, in this line:
bigHash[string1] = string1_hash

What were you *trying* to do?

Right, that example I'm giving is incorrect - I want to set them like
this (sorry, more perl-ish-ness):

bigHash[string1] = ${string1}_hash
bigHash[string2] = ${string2}_hash
...

In my example, the local variable named string1 is changing each time we
go through the loop, so what is actually happening is like this (still
not doing what I want, assigning the same hash to each unique key):

bigHash["12345"] = string1_hash
bigHash["54241"] = string1_hash
bigHash["86744"] = string1_hash
...
Also, don't you have this upside down? I thought we agreed you were
going to have *one* key called string1, whose value was an array, and
you were going to put 5 different things into that array. I don't see
you doing any of that.

m.

In bigHash I'm going to have one key for string1 (actually equal to
string1, "12345") but an arbitrary number of keys like that - in my
example (and hopefully clearer above) I have 3 keys each consisting of a
unique string. I want each of their values to be a hash that contains a
single key that evaluates to a different string, and the value of that
key is the array containing one or more yet different strings.

But I can't even get to the point where I have a unique hash as the
value for each of my keys - after doing more reading I think I should be
using the #{string1}_hash syntax to conveniently name my pointers to the
unique hashes I'm creating each time through my iteration. Does that
make more sense? Sorry my attempts to simplify my example have been
confusing.

Thanks for your help so far Matt, it is really helping me to "discuss"
it with another person who knows more about Ruby than I do.
 
M

matt neuburg

Peter Buckley said:
But I can't even get to the point where I have a unique hash as the
value for each of my keys

big_hash = Hash.new
%w{hey ho nonny no}.each {|k| big_hash[k] = Hash.new}

m.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,981
Messages
2,570,188
Members
46,731
Latest member
MarcyGipso

Latest Threads

Top