Ruby on Rails: How to pluck hash from a string - ruby-on-rails

I have a record of class String something like:
"{'1'=>'abc', '2'=> 'def'}"
but from this i need this in class Hash, something like:
{'1'=>'abc', '2'=> 'def'}
how to convert like this?
Edit: in my case i was getting this from CSV from other service where they were converting Hash into String before sending, so i asked them to send it using Base64.encode64 and now i am able to decode and get it in Hash format.

If you trust that the data is not nefarious you can write
str = "{'1'=>'abc', '2'=> 'def'}"
h = eval str
#=> {"1"=>"abc", "2"=>"def"}
See Kernel#eval.
If the data could be nefarious it would be safer to write
str.gsub(/{ *| *\|'/, '')
.split(/ *, */)
.map { |s| s.split(/ *=> */) }
.to_h
#=> {"1"=>"abc", "2"=>"def"}

This answer might not related to the question that i asked, but this is what solved my case, since other service was sending few Hash records in CSV format, they were converting the Hash to String before posting it to me.
So i asked them to use Base64.encode64 and send the encoded string in CSV.
Base64.encode64({'1'=>'abc', '2'=> 'def'}.to_json).delete("\n")
#=> "eyIxIjoiYWJjIiwiMiI6ImRlZiJ9"
and when i get the string i am converting it back to Hash something like
JSON.parse(Base64.decode64("eyIxIjoiYWJjIiwiMiI6ImRlZiJ9"))
#=> {"1"=>"abc", "2"=>"def"}

If I were writing a parser for objects that were in Ruby's object notation, not Javascript's, I'd call it either RON or RSON. I like the pun in the second better, so I think I'd go with RSON.
A quick google confirms that at least one other person thinks like I do, or at least arrives at the same conclusion: https://github.com/tannevaled/rson. So one option would be to use the rson gem, if it still works.
Alternatively, if you're not expecting any quotes or => to appear in any of the strings, you'd probably get away with something like
h = JSON.parse(str.gsub(/\s*=>\s*/, ":").gsub("'", '"'))

Related

Parse Hash in Ruby

How can I parse a hash in ROR?
I have a hash in string format(enclosed by double quotes) and i need to parse them to a valid hash.
eg.
input_hash = "{"name" => "john"}"
desired
output_hash = {"name" => "john"}
This is the wrong approach. String representation of a ruby hash is not a good way to serialise data. It is well structured, and definitely possible to get it back to a ruby hash (eval), but it's extremely dangerous and can give an attacker who has control over the input string full control over your system.
Approach the problem from a different angle. Look for where the string gets stored and change the code there instead. Store it for example as JSON. Then it can easily and safely be parsed back to a hash and can also be sent to systems running on something that is not ruby.

Unable to convert url params string to hash and then back to url string

Input is a params string (Input format cannot be changed)
something=1,2,3,4,5&something_else=6,7,8
Expected Output:
something=1,2,3,4,5&something_else=6,7,8
What I am doing:
params = 'something=1,2,3,4,5'
CGI::parse(params)
CGI.unescape(CGI.parse(params).to_query)
And I am getting this as output:
something[]=1,2,3,4,5
When I did CGI::parse(params)
I am getting this : {"something"=>["1,2,3,4,5"]}
which is wrong because it is not an array, something is a string which is "1,2,3,4,5" but it is being converted as array when I did CGI parse.
The reason I need to do CGI parse is because I need to manipulate the url PARAMS.
Is there any other possible way where I can convert it in the right way and maintain the params format?
The CGI module is a complete dinosaur and should probably be thrown in the garbage because of how bad it is, but for some reason it persists in the Ruby core. Maybe some day someone will refactor it and make it workable. Until then, skip it and use something better like URI, which is also built-in.
Given your irregular, non-compliant query string:
query_string = 'something=1,2,3,4,5&something_else=6,7,8'
You can handle this by using the decode_www_form method which handles query-strings:
require 'uri'
decoded = URI.decode_www_form(query_string).to_h
# => {"something"=>"1,2,3,4,5", "something_else"=>"6,7,8"}
To re-encode it you just call encode_www_form and then force unescape to undo what it's correctly doing to handle the , values:
encoded = URI.unescape(URI.encode_www_form(decoded))
# => "something=1,2,3,4,5&something_else=6,7,8"
That should get the effect you want.

Array is stored as string. How do I convert it back to any array?

I am using Rails 4.2 with Redis. When I store and retrieve an array with Redis, it returns a formatted string. How do I return this string to an array?
The returned string is exactly this, though the number and value of entries will obviously vary:
"[\"FCF1115A\", \"FCF1116A\"]"
Obviously, it could be parsed, but is there some function that would handle this? I could probably format a better string myself, rather than allowing Redis to do so. Thanks...
If using JSON library is an option. Following is tested in irb:
> require 'json'
> puts JSON.parse("[\"FCF1115A\", \"FCF1116A\"]").to_json
=> ["FCF1115A","FCF1116A"]

check if string is base64

i may recieve these two strings:
base = Base64.encode64(File.open("/home/usr/Desktop/test", "rb").read)
=> "YQo=\n"
string = File.open("/home/usr/Desktop/test", "rb").read
=> "a\n"
what i have tried so far is to check string with regular expression i-e. /([A-Za-z0-9+\/]{4})*([A-Za-z0-9+\/]{4}|[A-Za-z0-9+\/]{3}=|[A-Za-z0-9+\/]{2}==$)/ but this would be very heavy if the file is big.
I also have tried base.encoding.name and string.encoding.name but both returns the same.
I have also seen this post and got regular expression solution but any other solution ?
Any idea ? I just want to get is the string is actually text or base64 encoded text....
You can use something like this, not very performant but you are guaranteed not to get false positives:
require 'base64'
def base64?(value)
value.is_a?(String) && Base64.strict_encode64(Base64.decode64(value)) == value
end
The use of strict_encode64 versus encode64 prevents Ruby from inadvertently inserting newlines if you have a long string. See this post for details.

Stripping the first character of a string

i have
string = "$575.00 "
string.to_f
// => 0.0
string = "575.00 "
string.to_f
// => 575.0
the value coming in is in this format and i need to insert into a database field that is decimal any suggestions
"$575.00 "
We did this so often we wrote an extension to String called cost_to_f:
class String
def cost_to_f
self.delete('$,').to_f
end
end
We store such extensions in config/initializers/extensions/string.rb.
You can then simply call:
"$5,425.55".cost_to_f #=> 5425.55
If you are using this method rarely, the best bet is to simply create a function, since adding functions to core classes is not exactly something I would recommend lightly:
def cost_to_f(string)
string.delete('$,').to_f
end
If you need it in more than one class, you can always put it in a module, then include that module wherever you need it.
One more tidbit. You mentioned that you need to process this string when it is being written to the database. With ActiveRecord, the best way to do this is:
class Item < ActiveRecord::Base
def price=(p)
p = p.cost_to_f if p.is_a?(String)
write_attribute(:price, p)
end
end
EDIT: Updated to use String#delete!
So many answers... i'll try to summarize all that are available now, before give own answer.
1. string.gsub(/[\$,]/, '')
string.gsub!(/^\$/, '')
2. string[1..-1]
3. string.slice(0) # => "ome string"
4. s/^.//
Why (g)sub and regexp Just for deleting a character? String#tr is faster and shorter. String#delete is even better.
Good, fast, simple. Power of reverse indexing.
Hm... looks like it returns "S". Because it is an alias to String#[]
Perl? /me is cheking question tags...
And my advice is:
What if you have not dollar, but yena? Or what if you don't even have anything before numbers?
So i'll prefer:
string[/\d.+/]
This will crop leading non-decimal symbols, that prevent to_f to work well.
P.S.: By the way. It's known, that float is bad practice for storing money amounts.
Use Float or Decimal for Accounting Application Dollar Amount?
You could try something like this.
string = string[1..-1] if string.match(/^\$/)
Or this.
string.gsub!(/^\$/, '')
Remember to put that backslash in your Regexp, it also means "end of string."
you can use regex for that:
s/^.//
As laways, this is PCRE syntax.
In Ruby, you can use the sub() method of the string class to replace the string:
result = string.sub(/^./,"")
This should work.
[EDIT]
Ok, someone asked what's the gsub() is for:
gsub() acts like sub() but with the /g modifier in PCRE (for global replacement):
s/a/b/
in PCRE is
string.sub(/a/, "b")
and
s/a/b/g
is
string.gsub(/a/, "b")
in Ruby
What I'd use (instead of regular expressions) is simply the built-in slice! method in the String class. For example,
s = "Some string"
s.slice!(0) # Deletes and returns the 0th character from the string.
s # => "ome string"
Documentation here.

Resources