Symbols vs. strings as hash_keys - interchangeable or not?

All,

I have a Rails app where I load a YAML file into a class variable (in my
application.rb file so it’s available to all controllers), like so:

@@config = YAML.load_file("#{RAILS_ROOT}/config/eSimplyConf.yml")

and I created a method to give me back this hash:

def config
@@config
end

However, I notice that when I call config[:key_name], I don’t get
anything, but when I call config[‘key_name’] I do get the value. I
further verified that if I called config[:key_name.to_s], I will get the
value, thus underscoring the distinction between symbol and string here
as the type of key_name.

I was under the impression that symbols and strings were interchangeable
for the purpose of addressing a hash element. Is this not the case? Is
my problem that YAML.load_file generates keys that are strings instead
of symbols, and of course, I’m using Rails, so that I expect all of my
hash keys to be symbols :)?

Thanks,
Wes

On Jul 22, 2006, at 12:50 PM, Wes G. wrote:

def config


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

Wes-

The symbol and string as hash key are specific to rails

implementation of HashWithIndifferetnAccess. Thi is a hash that
responds to string and symbol keys in the same way. This is not the
behavior of the standard ruby Hash class. If you want this behavior
then make your config hash be a HashWithIndifferentAccess instead of
a normal hash. You can do this by changinf the header in your yaml
file to this:

— !map:HashWithIndifferentAccess
foo: bar
bar: baz

-Ezra

Ezra,

Is it done in Rails like this for performance reasons (I’m guessing
symbols may be slightly more lightweight than Strings)?

Thanks,
Wes

Wes G. wrote:

@@config

my problem that YAML.load_file generates keys that are strings instead
of symbols, and of course, I’m using Rails, so that I expect all of my
hash keys to be symbols :)?

As Ezra points out they are not the same in Ruby. A hash key can be any
object whatsoever. It could even be a number or another hash:

{ {:a=>1} => ‘yep’ }

Since symbols and strings are not the same thing, they likewise do not
represent the same keys. Versitle Ruby’s hash is. Common it is not.
Lets be honest, 80% of the time, if not more we simply want a “keyword”
representation for out hash keys. Ruby’s hash does not make that
straigiht foward and no doubt we’ve all spent too much time fusing with
to_s and to_sym in this regard.

I’ve offered some ideas for improvement in this area, symbol to string
coercion for instance. But that seems to be just as problematic. My
last consideration was a way to “normalize keys on the fly”

h = Hash.new.key_by{ |k| k.to_sym }
h[“x”] = 1
h #=> {:x=>1}

I point out one last issue in this regard though, is that I have found
that I generally want my keys as symbols in code, but when I dump or
load YAML I want them as strings. Two extra time-consuming steps to do
this, so I end up just blowing wads of memory and sticking with
strings. :frowning:

Anyhow sorry to ramble on. But I would be interested it what others
have thoughtof for dealing with this.

As a solution in your case you might be interested in what I often do,
use Facets OpenCascade. It can load simple YAML configs into a nice
method-based access object:

s = %{

a: “a”
b:
x: “x”
}

oc = OpenCascade.new( YAML.load(s) )

oc.a #=> “a”
oc.b.x #=> “x”

T.

Hey,

On 7/22/06, Ezra Z. [email protected] wrote:

    The symbol and string as hash key are specific to rails

implementation of HashWithIndifferetnAccess. Thi is a hash that
responds to string and symbol keys in the same way. This is not the
behavior of the standard ruby Hash class. If you want this behavior
then make your config hash be a HashWithIndifferentAccess instead of
a normal hash. You can do this by changinf the header in your yaml
file to this:

I’ve actually been wondering about this myself lately… (just started
playing w/ rails for a webapp i had to write a couple days ago). What
is the point of using symbols for the hash keys? Is that just an
aesthetic thing? or does it have to be this way?

Thanks,
Cam

On 7/22/06, [email protected] [email protected] wrote:

As Ezra points out they are not the same in Ruby. A hash key can be any
object whatsoever. It could even be a number or another hash:

{ {:a=>1} => ‘yep’ }

Not meaning to be picky :wink: but your example is a bit misleading.
Consider:

h1 = { {:a => 1} => ‘nope’}
p h1[{:a => 1}]
#=> nil
hk = {:a => 1}
h2 = {hk => ‘yep’}
p h2[hk]
#=> “yep”

The problems of using hashes as keys has come up a few times - see
e.g. ruby-talk:167185 for one solution. Or you could just use:

class Hash
def hash
self.to_a.hash
end
def eql?(other)
hash == other.hash
end
end

h = {{ :a => 1, :b => 2 } => ‘yep’}
p h[:a => 1, :b => 2]
#=> “yep”

though to be honest I’m not sure if this has unpleasant side-effects
on any libs.

Regards,
Sean

Yes, :symbols use less memory than “Strings”, but
HashWithIndifferentAccess(which lets you use either) is quite slow
compared
to a normal Hash.

j`ey
http://www.eachmapinject.com

Sean O’Halpin wrote:

#=> nil
hk = {:a => 1}
h2 = {hk => ‘yep’}
p h2[hk]
#=> “yep”

The problems of using hashes as keys has come up a few times - see
e.g. ruby-talk:167185 for one solution. Or you could just use:

Huh? I didn’t know that. Why isn’t one hash “eql?” to another that has
“eql?” elements?

T.

Not to be dense, but why would you use a hash as a hash key in the first
place?

I guess it could “save you” another level of indirection, but I would
think the code would be so confusing as to not be worth it.

Wes

On 7/23/06, Trans [email protected] wrote:

h1 = { {:a => 1} => ‘nope’}
Huh? I didn’t know that. Why isn’t one hash “eql?” to another that has
“eql?” elements?

T.

Because Hash#hash == Hash#object_id and st_lookup() uses this and eql?
to test for equality (or rather inequality). At least, that’s my
understanding of the relevant code in st.c:

#define PTR_NOT_EQUAL(table, ptr, hash_val, key)
((ptr) != 0 && (ptr->hash != (hash_val) || !EQUAL((table), (key),
(ptr)->key)))

where EQUAL is defined as:

#define EQUAL(table,x,y) ((x)==(y) || (*table->type->compare)((x),(y))
== 0)

I agree that it does seem a little counter-intuitive to not have
Hash#eql? compare by value (like Array#eql? does).

Regards,
Sean