Converting hash keys to symbols

Golf question...

Is there an elegant way of converting all of the keys of a hash to a symbol? The solution can assume that the existing keys are either strings or symbols. Also, if a key is a string, it may contain a dash character which must be converted to an underscore.

Something like this, only more elegant, would be nice:

   a = some_hash_of_strings_and_symbols
   new_a = Hash[*a.collect { |k,v|
     k = k.gsub(/-/,"_").intern if k.is_a?(String)
     [k,v] }.flatten]

Any takers?

- Jamis

···

--
Jamis Buck
jgb3@email.byu.edu
http://www.jamisbuck.org/jamis

But you should not do this for random strings, as symbols are AFAIK not
GC'ed.

How about this (converts all symbols to strings, which is easier and
more secure, as strings get GC'ed):

  h = Hash.new
  a.each {|k,v| h[k.to_s.tr('-','_')] = v}
  h

Regards,

  Michael

···

On Wed, Nov 03, 2004 at 06:35:59AM +0900, Jamis Buck wrote:

Golf question...

Is there an elegant way of converting all of the keys of a hash to a
symbol? The solution can assume that the existing keys are either
strings or symbols. Also, if a key is a string, it may contain a dash
character which must be converted to an underscore.

Something like this, only more elegant, would be nice:

  a = some_hash_of_strings_and_symbols
  new_a = Hash[*a.collect { |k,v|
    k = k.gsub(/-/,"_").intern if k.is_a?(String)
    [k,v] }.flatten]

Any takers?

Jamis Buck wrote:

Golf question...

Is there an elegant way of converting all of the keys of a hash to a symbol? The solution can assume that the existing keys are either strings or symbols. Also, if a key is a string, it may contain a dash character which must be converted to an underscore.

Something like this, only more elegant, would be nice:

  a = some_hash_of_strings_and_symbols
  new_a = Hash[*a.collect { |k,v|
    k = k.gsub(/-/,"_").intern if k.is_a?(String)
    [k,v] }.flatten]

Any takers?

- Jamis

I'd be inclined to add 'intern' to Symbol, and just call intern on everything in the key set. If you get a Symbol, then Symbol#intern just returns self.

And override intern in String to do character subs.

James

Jamis Buck wrote:

Is there an elegant way of converting all of the keys of a hash to a symbol? The solution can assume that the existing keys are either strings or symbols.

Does this help?

a = Hash.new do |hash, key|
   hash.fetch(key.is_a?(Symbol) ? key.to_s : key.to_sym, nil)
end

a["one"] = "eins"
a[:two] = "zwei"

a["one"] == a[:one] # => true
a["two"] == a[:two] # => true

Also, if a key is a string, it may contain a dash character which must be converted to an underscore.

Why? Symbols can contain dash characters. See :"foo-bar", %s{foo-bar}, "foo-bar".intern and "foo-bar".to_sym.

Also note that #to_sym is defined for Symbol as well.

So maybe you could also do something like this (in case the above doesn't work for you):

result = Hash.new(&a.default_proc)
until a.empty?
   key, value = *a.shift
   result[key.to_sym] = value
end
a.replace(result)

Michael Neumann wrote:

But you should not do this for random strings, as symbols are AFAIK not
GC'ed.

Good point. I hadn't considered that. However, for what I'm doing, there are no more than a double-handful of unique, valid strings and symbols, so I don't think memory is an issue.

How about this (converts all symbols to strings, which is easier and
more secure, as strings get GC'ed):

  h = Hash.new
  a.each {|k,v| h[k.to_s.tr('-','_')] = v}
  h

Ah, #tr is what I was looking for. Thanks! That's one step towards making it more elegant, anyway.

Thanks, Michael!

- Jamis

···

--
Jamis Buck
jgb3@email.byu.edu
http://www.jamisbuck.org/jamis

James Britt wrote:

I'd be inclined to add 'intern' to Symbol, and just call intern on everything in the key set. If you get a Symbol, then Symbol#intern just returns self.

And override intern in String to do character subs.

Good point. However, this is something that is only occurring at one point in the code, and opening the classes to (re)define the methods is a lot more lines of code than I'm up for at this point. :slight_smile:

- (lazy) Jamis

···

--
Jamis Buck
jgb3@email.byu.edu
http://www.jamisbuck.org/jamis

Florian Gross wrote:

Jamis Buck wrote:

Is there an elegant way of converting all of the keys of a hash to a symbol? The solution can assume that the existing keys are either strings or symbols.

Does this help?

a = Hash.new do |hash, key|
  hash.fetch(key.is_a?(Symbol) ? key.to_s : key.to_sym, nil)
end

Clever! That's really nice. It won't quite work for me, however, since I don't have control over the creation of the hash that I'm receiving.

Also, if a key is a string, it may contain a dash character which must be converted to an underscore.

Why? Symbols can contain dash characters. See :"foo-bar", %s{foo-bar}, "foo-bar".intern and "foo-bar".to_sym.

Yah, I know. But I like to use symbols so I don't *have* to type the quotes. :slight_smile: It's a matter of convenience rather than optimization, in this case.

Thanks for the suggestions, Florian. Thanks, especially, for pointing out #to_sym. I wasn't aware of that, and I like it better than the less-intuitive #intern.

- Jamis

···

--
Jamis Buck
jgb3@email.byu.edu
http://www.jamisbuck.org/jamis