17

I have this string and I'm wondering how to convert it to a Hash.

"{:account_id=>4444, :deposit_id=>3333}"
sawa
  • 165,429
  • 45
  • 277
  • 381
zoras
  • 943
  • 1
  • 9
  • 19
  • 1
    try this: http://stackoverflow.com/questions/1667630/how-do-i-convert-a-string-object-into-a-hash-object – yaka Dec 23 '11 at 08:58

5 Answers5

17

The way suggested in miku's answer is indeed easiest and unsafest.

# DO NOT RUN IT
eval '{:surprise => "#{system \"rm -rf / \"}"}'
# SERIOUSLY, DON'T

Consider using a different string representation of your hashes, e.g. JSON or YAML. It's way more secure and at least equally robust.

Luc
  • 5,339
  • 2
  • 48
  • 48
Jan
  • 11,636
  • 38
  • 47
  • 1
    nop these are rails post params so i guess i can't use json or yaml – zoras Dec 23 '11 at 09:46
  • 1
    Interesting. Controllers in Rails usually receive `POST` params as Ruby objects. How come you get them as a string? – Jan Dec 23 '11 at 09:51
  • 5
    lets hope no one runs that to "see what it will do" :-/ – Pavling Dec 23 '11 at 11:04
  • @Pavling, good point. I've added an appropriate piece of advice. – Jan Dec 23 '11 at 11:11
  • Actually, I'm sending these as [custom options for paypal](https://cms.paypal.com/us/cgi-bin/?cmd=_render-content&content_ID=developer/e_howto_html_Appx_websitestandard_htmlvariables) like this `:custom => {:account_id => @account.id, :deposit_id => @deposit.id}`. So, when paypal posts back the IPN the hash is quoted as string in the controller. Is there any other way to send multiple variables with paypal custom options altogether? – zoras Dec 23 '11 at 13:54
  • @zoras, I guess it's a candidate for a new question with a [tag:paypal] tag. – Jan Dec 23 '11 at 14:27
  • 19
    this takes really long when I run it – nurettin Oct 23 '13 at 14:09
  • 3
    yeah maybe at least explain what the code you wrote does. i feel like it's v possible someone new to ruby/coding might run it anyway just to see. so for that person: it would delete all of your files. – volx757 Jan 22 '16 at 18:02
  • You should add `--no-preserve-root` to your example to really kick balls! :-P – Chris Suszyński May 12 '16 at 08:43
  • don't forget to add `sudo` before `system` – Michael Feb 20 '18 at 23:54
16

With a little replacement, you may use YAML:

require 'yaml'

p YAML.load(
  "{:account_id=>4444, :deposit_id=>3333}".gsub(/=>/, ': ')
  )

But this works only for this specific, simple string. Depending on your real data you may get problems.

knut
  • 27,320
  • 6
  • 84
  • 112
  • Worked like a charm! I passed mine into `HashWithIndifferentAccess.new` to get a hash like the param hash. – Josh Oct 06 '16 at 00:25
  • This would only work with data types that are native to YAML. A regular expression, for example, wouldn't be parseable in this way. – Hosam Aly Dec 06 '21 at 11:53
12

if your string hash is some sort of like this (it can be nested or plain hash)

stringify_hash = "{'account_id'=>4444, 'deposit_id'=>3333, 'nested_key'=>{'key1' => val1, 'key2' => val2, 'key3' => nil}}"

you can convert it into hash like this without using eval which is dangerous

desired_hash = JSON.parse(stringify_hash.gsub("'",'"').gsub('=>',':').gsub('nil','null'))

and for the one you posted where the key is a symbol you can use like this

JSON.parse(string_hash.gsub(':','"').gsub('=>','":'))
aalaap
  • 4,145
  • 5
  • 52
  • 59
Mr. Bless
  • 285
  • 4
  • 10
11

The easiest and unsafest would be to just evaluate the string:

>> s = "{:account_id=>4444, :deposit_id=>3333}"
>> h = eval(s)
=> {:account_id=>4444, :deposit_id=>3333}
>> h.class
=> Hash
miku
  • 181,842
  • 47
  • 306
  • 310
  • I'm getting these in rails post params. Isn't there a safe way to convert string into hash other than eval. – zoras Dec 23 '11 at 09:45
  • 1
    I don't know your setup, but maybe it would be easier to get back a hash in the first place. Otherwise, ruby supports some TAINT levels, see: http://www.ruby-doc.org/docs/ProgrammingRuby/html/taint.html – miku Dec 23 '11 at 09:54
4

Guess I never posted my workaround for this... Here it goes,

# strip the hash down
stringy_hash = "account_id=>4444, deposit_id=>3333"

# turn string into hash
Hash[stringy_hash.split(",").collect{|x| x.strip.split("=>")}]
zoras
  • 943
  • 1
  • 9
  • 19
  • 6
    This will fail to split fields correctly if you have any data that contains a `,` or `=>`. `{ :text => "Welcome, friends.", delim => "=>" }` – Matt Jul 19 '14 at 12:28