Convert string into a variable object

Hi,
I have a string named “ruby”. I want to create an empty object by that
name: @ruby

Is there a way to do this?

Ad Ad wrote:

Hi,
I have a string named “ruby”. I want to create an empty object by that
name: @ruby

Is there a way to do this?

Yes.
Also, it is most likely that there is an easier way to do what you want
to do.
What do you want to do?

Aldric G. wrote:

Ad Ad wrote:

Hi,
I have a string named “ruby”. I want to create an empty object by that
name: @ruby

Is there a way to do this?

Yes.
Also, it is most likely that there is an easier way to do what you want
to do.
What do you want to do?

I have a file with a list of cities in it. The file is a daily feed and
the cities in it change daily.

I want to create a class which reads the file and creates the variables
with the city names.
File.open(“file”).each{ |x|
@“x” = 1 #I need a logic for this
end

this would give us @toronto = 1, @miami = 1, etc.

I have subclasses which would read user input and if that city variable
has been defined already then move ahead.
it would be a simple if statement
if @toronto

end

I could create a hash map too but this is the easiest way to do since it
doesnt require the creation of any sort of a container variable.

On Tue, Dec 8, 2009 at 3:35 AM, Ad Ad [email protected] wrote:

to do.

doesnt require the creation of any sort of a container variable.

Posted via http://www.ruby-forum.com/.

you can use

File.open(“file”).each{ |x|
instance_variable_set “@#{x}”, 1
end

On 07 Dec 2009, at 21:35, Ad Ad wrote:

to do.

doesnt require the creation of any sort of a container variable.

Posted via http://www.ruby-forum.com/.

A much better way to do this would be to store the city names in a hash.
your code would then be:

File.open(@file). each do |x|
@hash[x] = true
end

later on you can then just do

if @hash[city]

Toon

On Monday 07 December 2009 02:35:39 pm Ad Ad wrote:

I have a file with a list of cities in it. The file is a daily feed and
the cities in it change daily.
[snip]
this would give us @toronto = 1, @miami = 1, etc.

Is 1 significant? That is, are you trying to count the number of cities,
or
are you just trying to set them to true?

I have subclasses which would read user input and if that city variable
has been defined already then move ahead.
it would be a simple if statement
if @toronto

end

In that case, it would be much more readable if you did

@toronto = true

rather than

@toronto = 1

They’ll do the same thing here, but consider this:

@toronto = 0

That would be false, right? Wrong. Anything that’s not false or nil is a
true
value in Ruby. You’re MUCH better off explicitly using true and false,
if
that’s really what you wanted.

If it was meant to be a count, you’d probably want something like:

if @toronto > 0

but I have no idea why a count would make sense here, anyway.

I could create a hash map too

Yes, you should do that. The downsides of using instance variables for
that
are much larger than the downsides of doing a little extra typing with:

if @cities[‘toronto’]

but this is the easiest way to do since it

No it’s not. You had to come here and ask if it was even possible. You
already
know a hash map is possible, and yo probably already know how to do it.

Just in case someone tells you how to use variables instead, here’s one
very
simple reason it’s a bad idea: Instance variables have symbols assigned
to
them. Last I checked, symbols aren’t garbage collected, so doing it your
way
will result in a (very small) memory leak in your program. That won’t
happen
with a hash of strings.

doesnt require the creation of any sort of a container variable.

Why is this a problem?

I mean, is it the creation of this that’s holding you back? That’s a
single
line:

@cities = {}

Put it in initialize.

Toon Willems wrote:

On 07 Dec 2009, at 21:35, Ad Ad wrote:

to do.

doesnt require the creation of any sort of a container variable.

Posted via http://www.ruby-forum.com/.

A much better way to do this would be to store the city names in a hash.
your code would then be:

File.open(@file). each do |x|
@hash[x] = true
end

Would it not be better to convert to symbols? That is:

File.open(@file). each do |x|
@hash[x.strip.to_sym] = true
end

if @hash[:toronto]
or
if @hash[:“palo alto”]

or

if @hash[city.to_sym]

later on you can then just do

if @hash[city]

Toon

On Tuesday 08 December 2009 01:03:31 am Steve W. wrote:

@hash[x] = true
end

Would it not be better to convert to symbols?

Not if the cities are changing every day. Remember, symbols are never
garbage
collected – they should only be used when there is a finite and
predictable
number of possible symbols you might create.

Otherwise, you have a slow memory leak – and especially for something
like
this, you’re not really going to get a performance boost out of it.

HI –

On Tue, 8 Dec 2009, Steve W. wrote:

A much better way to do this would be to store the city names in a hash.
end

if @hash[:toronto]
or
if @hash[:“palo alto”]

or

if @hash[city.to_sym]

In addition to the garbage collection issue that David M. pointed out,
symbols seem an odd choice to me for city names. I always think of
symbols as appropriate for label-like functionality and values taken
from a limited set. Strings are a better choice for general
representation of text.

David

2009/12/8 David A. Black [email protected]:

doesnt require the creation of any sort of a container variable.

or

if @hash[city.to_sym]

In addition to the garbage collection issue that David M. pointed out,
symbols seem an odd choice to me for city names. I always think of
symbols as appropriate for label-like functionality and values taken
from a limited set. Strings are a better choice for general
representation of text.

I would certainly use String for city names. Hard encoding city names
in classes which would be regenerated over and over again is not a
good solution. Basically, you want to stick all city names as keys in
a Hash and work from there. The mere fact that Ruby is able to
generate local, instance or global variables at runtime does not mean
it is a proper means in all cases. The use case at hand does not call
for meta programming - it just needs a data structure which can work
with appropriate key values. A Hash seems like a good fit here.

My 0.02EUR.

Kind regards

robert

David M. wrote:

On Tuesday 08 December 2009 01:03:31 am Steve W. wrote:

@hash[x] = true
end

Would it not be better to convert to symbols?

Not if the cities are changing every day. Remember, symbols are never
garbage
collected – they should only be used when there is a finite and
predictable

I would have thought number of cities over time would be finite and
predictable. Granted, the number of cities is probably in the tend or
hundreds of thousands.

So symbols would be appropriate if instead of cities, adad was reading a
text file of state names? How about country names (currently under 200)?
I ask, in an attempt to gauge what is typically considered the accepted
threshold for using symbols.

number of possible symbols you might create.

Otherwise, you have a slow memory leak – and especially for something
like
this, you’re not really going to get a performance boost out of it.

On 07 Dec 2009, at 21:35, Ad Ad wrote:
your code would then be:

symbols seem an odd choice to me for city names. I always think of
for meta programming - it just needs a data structure which can work
with appropriate key values. A Hash seems like a good fit here.

Now perhaps I’m missing something, but the impression I got was that
while the contents of the file changes daily, he doesn’t want to remove
old cities. Rather he wants to ensure he’s not creating duplicates. My
solution (to the problem as I understand it) would be to use sets, but
if he’s not actually wanting to remove anything, I don’t think Strings
vs. Symbols should matter.

Example of set solution:
irb(main):001:0> require ‘set’
=> true
irb(main):002:0> cities=Set.new
=> #<Set: {}>
irb(main):003:0> cities << ‘Austin, Texas’
=> #<Set: {“Austin, Texas”}>
irb(main):004:0> cities << ‘Boring, Oregon’
=> #<Set: {“Austin, Texas”, “Boring, Oregon”}>
irb(main):005:0> cities << ‘Boise, Idaho’
=> #<Set: {“Austin, Texas”, “Boring, Oregon”, “Boise, Idaho”}>
irb(main):006:0> cities << ‘Boring, Oregon’
=> #<Set: {“Austin, Texas”, “Boring, Oregon”, “Boise, Idaho”}>
irb(main):008:0> cities.include? ‘Boring, Oregon’
=> true
irb(main):009:0>

Is there something I’m missing?

Steve W. wrote:

I would have thought number of cities over time would be finite and
predictable. Granted, the number of cities is probably in the tend or
hundreds of thousands.

So symbols would be appropriate if instead of cities, adad was reading a
text file of state names? How about country names (currently under 200)?
I ask, in an attempt to gauge what is typically considered the accepted
threshold for using symbols.

That’s certainly easy enough to create - get some list of cities…
http://answers.google.com/answers/threadview/id/774429.html
http://answers.google.com/answers/threadview/id/779905.html
http://www.maxmind.com/app/worldcities

Parse it… Make symbols… Check our how your computer’s RAM looks, and
how well it handles the hash / array / whatever data structure you’ve
been using (if any, since you can just create symbols, after all…)

Let us know :wink:

On Tuesday 08 December 2009 02:11:53 pm Steve W. wrote:

David M. wrote:

text file of state names?
Nope. A typo or an error in the file, and you’ve got a problem again.
It’s
similar to when you’ve got any sort of external input which you want to
compare to a finite list of values. It might be tempting to do this:

Values = [:one, :two, :three, :four]

if Values.include? input_value.to_sym

What you should be doing is this:

Values = [‘one’,‘two’,‘three’,‘four’].map(&:freeze).freeze

if Values.include? input_value

If it’s still not efficient enough (if there are hundreds of values),
put them
in a Set or a Hash, but that’s even more reason not to convert input to
syms.

I’d only do it that way you’re suggesting if the file in question was
part of
the source distribution, but it sounds like it’s coming from an external
service.

How about country names (currently under 200)?
I ask, in an attempt to gauge what is typically considered the accepted
threshold for using symbols.

In my opinion, the threshold of using symbols is whenever it’s a finite
number
that’s generated only from trusted sources – generally, stuff inside
your
application source code.

It also matters how it’s being used – as David Black and Robert K.
point
out, symbols are generally for labels. They’re what’s used to refer to
functions and variables by “name” in Ruby. Their other major use is for
hashes
of options passed around – essentially, keyword arguments.

It might also help to think of Symbols as Enum values. Let me put it
this way
– in other languages, like C and Java, you might have a fixed number of
values you might want to work with. For example, suppose I want to open
a file
read, write, or both. It’s inefficient to actually pass the strings
“read”,
“write”, or “read/write” with every file open, so instead, I might pass
an
integer, 1, 2, or 3. But that’s annoying to work with, so instead, I’d
define
a constant:

#define READ 1
#define WRITE 2
#define READWRITE 3

Now I can do something like:

open(“foo”, READ)

Then, inside the open function, you’d have something like:

case mode
when READ
when WRITE

All of which is just shorthand for:

open(“foo”, 1)

and

case mode
when 1
when 2

This is vastly oversimplified, and not how it’s actually done, but it
works.
This is also such a common pattern that languages have shortcuts for it.
I’m
working from memory here, so the syntax is probably wrong, but the idea
holds:

enum { READ, WRITE, READWRITE }

open(“foo”, READ)

The enum will automatically assign a unique integer value to each of
READ,
WRITE, and READWRITE. As long as that same enum is visible to the code
of the
open function, and to the code calling it, the number assigned to READ,
WRITE,
and READWRITE will be the same each time.

Note that at this point, you really don’t have to care what number it
is, just
that it’s unique, and that doing it this way is just as efficient as
manually
specifying a number.

And since it doesn’t matter, there’s no reason passing 1 should be more
efficient than passing 3085, or anything, as long as it’s still a 32-bit
integer. (Or 64-bit, if you’re on a 64-bit platform.) If you were really
strapped for space, you could use a single byte value, but there is
actually a
real possibility that won’t be enough, depending on your application,
and you
want to be backwards compatible. So an int makes sens, and besides, enum
is
doing all the work for you.

So, symbols – a concept from Lisp, actually – take this just a step
farther.
Rather than assigning a number that’s just unique for that function
(READ-1,
WRITE-2, etc), you get a number that’s globally unique. When you type

:foo

…what you’re really doing is getting a unique integer, which Ruby will
replace any occurrance of the symbol :foo with, anywhere in your source
code.
Again, it’s oversimplifying – it’s probably implemented as an integer,
but
you’ll see it as a Symbol object. The entire point of this is so that
you can
guarantee that the following two things will be true:

:foo == :foo
:foo != :bar

And of course, you can do case structures using symbols, you can use
them in
hash values, and so on.

And because Ruby is reflective, you can do things like:

“foo”.to_sym

But scroll back up and look at the “enum” example. This kind of monkying
is
properly metaprogramming – it would be like writing a program that
generates
enum statements for a header.

Sometimes, it might actually be appropriate. An obvious example is an
ORM –
an intelligent ORM can read the database schema and create methods named
after
database columns. You’ll get those column names as strings, and you’ll
turn
them into symbols. You might even be fancy, like Rails, and do some
string
manipulation (pluralize them, etc) and create some more methods.

That’s not entirely a new idea, either. If this was a compiled language,
you’d
probably have a tool that took some specification (maybe XML… ugh) and
converted it into both SQL statements to create that database, and
source code
to access it. The main difference is that Ruby is dynamic enough to do
this at
runtime, just-in-time, rather than having to actually generate source
code.

But the concepts are the same.

Here’s a quick rule of thumb:

  • Am I metaprogramming?
  • Are these keyword arguments, or some sort of options hash?
  • Are the symbols created with the colon notation (:foo)?

If you answered yes to any of those, symbols are fine. If you answered
no to
all of them, probably not.

phew! I think I need a blog.

On 12/08/2009 09:57 PM, Walton H. wrote:

On 07 Dec 2009, at 21:35, Ad Ad wrote:
File.open(@file). each do |x|
if @hash[:“palo alto”]
I would certainly use String for city names. Hard encoding city names
old cities. Rather he wants to ensure he’s not creating duplicates. My
=> #<Set: {“Austin, Texas”}>
Is there something I’m missing?
Maybe. I was talking about the approach to generate variable names.
City names in this case are part of the domain data. They are read over
and over again and apparently the task of the program is to manage city
names somehow.

Variable names on the other hand are part of the application structure.
You’d rather have a variable @city_name here than a variable
@new_york. It does not make much sense to regenerate classes over and
over again (at least that seems to be what OP wants to do) does not
really make sense.

The question what is added or removed which you discussed is a
completely different one. Whether a Set or a Hash is a more appropriate
data structure depends on the use case which I haven’t seen much detail
of. (Maybe I’m missing something as well.)

Kind regards

robert

Hi,

I would think it depends entirely on the use case.

You’re going to need to hit 8MB memory usage before ruby is going to
sweep and collect those string objects.

Every time you access a value in your hash by key, that is a new string
object. It could be more efficient to use symbols to stay under a
threshold, rather then go over it by creating new string objects every
time you want to access a value in your hash.

Thanks,
Rob