parsing in ruby - ruby

I have this Hash:
cookie = {"fbs_138415639544444"=>["\"access_token=138415639544444|5c682220fa7ebccafd97ec58-503523340|9HHx3z7GzOBPdk444wtt&expires=0
&secret=64aa8b3327eafbfd22ba070b&session_key=5c682220fa7dsfdsafas3523340
&sig=4a494b851ff43d3a58dfa8757b702dfe&uid=503523340\""],
"_play_session"=>["fdasdfasdf"]}
I need to get the substring from right after access_token= to right before &expires. The problem is that the number in the key fbs_138415639544444 changes every time, just the part fbs_ remains constant.
Any idea how to only get:
"138415639544444|5c682220fa7ebccafd97ec58-503523340|9HHx3z7GzOBPdk444wtt"

This is a common task when decoding parameters and queries in HTML URLs. Here's a little method to break down the parameters into a hash. From there it's easy to get the value you want:
def get_params_hash(params)
Hash[ *params.split('&').map{ |q| q.split('=') }.flatten ]
end
p get_params_hash(cookie['fbs_138415639544444'].first)['"access_token']
# >> "138415639544444|5c682220fa7ebccafd97ec58-503523340|9HHx3z7GzOBPdk444wtt"
In Ruby 1.9+, hashes retain their insertion order, so if the hash always has the value you want as its first entry, you can use
cookie.keys.first #=> "fbs_138415639544444"
otherwise use:
cookie.keys.select{ |k| k[/^fbs_/] }.first #=> "fbs_138415639544444"

I never code in ruby, but this sounds like a typical task for split function.
you just need to split this
"\"access_token=138415639544444|5c682220fa7ebccafd97ec58-503523340|9HHx3z7GzOBPdk444wtt&expires=0
&secret=64aa8b3327eafbfd22ba070b&session_key=5c682220fa7dsfdsafas3523340
&sig=4a494b851ff43d3a58dfa8757b702dfe&uid=503523340\""
by & symbol. The first element of result array will be:
"\"access_token=138415639544444|5c682220fa7ebccafd97ec58-503523340|9HHx3z7GzOBPdk444wtt"
and after split it by =, and the second element of result array should be:
138415639544444|5c682220fa7ebccafd97ec58-503523340|9HHx3z7GzOBPdk444wtt

If you only need the access_key part, then a regex is probably easiest.
cookie["fbs_138415639544444"][0] =~ /access_token\=([-\w\d\|]*)&/
access_key = $1
Here the access_key is in the first capture group and you can get it with $1.
A better option if you'll need other parts of the string (say the session_key), would probably be to use a couple splits and parse the string into it's own hash.
Edit: Just realized you need the key too.
key = cookie.each_key.find { |k| k.start_with? "fbs_" }
Then you can use key to get the value.

Since the key changes, the first step is to get right key:
key = cookie.keys.select {|k| k =~ /^fbs_/}.first
This matches them if they begin with the text "fbs_". The first match is returned.
Next you can get the other value by a few (ugly) splits:
cookie[key].first.split('=')[1].split('&').first
Using a regex might be a bit cleaner, but it depends on what the valid characters are in that string.

Regexs are brittle so I wouldn't use those when the reality is you are parsing query string params in the end so use the CGI lib:
> require 'cgi'
=> true
> cookie = {"fbs_138415639544444"=>["\"access_token=138415639544444|5c682220fa7ebccafd97ec58-503523340|9HHx3z7GzOBPdk444wtt&expires=0&secret=64aa8b3327eafbfd22ba070b&session_key=5c682220fa7dsfdsafas3523340&sig=4a494b851ff43d3a58dfa8757b702dfe&uid=503523340\""], "_play_session"=>["fdasdfasdf"]}
> CGI.parse(cookie.select {|k,v| k =~ /^fbs_/}.first[1][0])["\"access_token"][0]
=> "138415639544444|5c682220fa7ebccafd97ec58-503523340|9HHx3z7GzOBPdk444wtt"

This is how i solved the problem...
access_token_key = cookies.keys.find{|item| item.starts_with?('fbs_') }
token = cookies[access_token_key].first
access_token = token.split("&").find{|item| item.include?('access_token') }
fb_access_token = access_token.split("=").find{|item| !item.include?('access_token') }

Related

Ruby pushing to a hash

I have a two part question and I apologize in advance if it is confusing at all. I'm trying to put user input into an empty hash. I know with an array you use the << to push the info to it. Is there a hash equivalent to this?
2nd part: Say I was just looping them the same question until a condition is met. The user input is going to be the value. Is there a way/method to make the key automatically change per the user input? So it would look something like:
{str1 => "example string", str2 => "example string2", str3 => "example string3"}
Or is there a way to have ruby assign a key on its own?
Sorry again if the second part is confusing. I know an array would be better but the little challenge I am working is asking for a hash.
Another way to add element to ruby hash store(key, value)
hash = {}
hash.store("first", 42)
hash #=> {"first"=>42}
With an array you use << to push a single element.
With a hash you are tracking not one element but two (both the key and value).
So for example:
my_key = "foo"
my_val = "bar"
my_hash = {}
my_hash[key] = val
Sure, you can do this in a loop.
I would recommend RubyMonk to learn more about this but their website is down. So I can recommend this gist which shows some examples or simply read the Hash section of any ruby tutorial.
Here are the two ways to add to a Hash
hash[str1] = "example string"
hash.merge!(str1 => "example string")
If you don't care about indexing on a key, as a Hash is intrinsically a key/value store, you probably want a Set:
require 'set'
set = Set.new
set << gets.chomp
A set is like a keyless hash, it's an un-ordered collection of things but with the side benefit that lookups for elements in the set are quick and they're also automatically uniqued, adding the same thing twice has no effect.
The alternative here is to put something in the Hash with the value as the key and any other value as a placeholder:
values = { }
values[input.gets] = true
This is like a Set but is probably less efficient to use if you don't care about values.
Ok, it isn't array so '<<' can't be work.
You should use this:
your_hash = {}
hash_key = "x"
hash_value = "y"
your_hash[:hash_key] = hash_value
It's all.

How to select keys from hash and strip parts of the keyname with ruby

I have a config hash in my ruby project and I want to pick certain keys with their value to have them as a separate hash.
Project.config.to_h.select{ |k,v| k[/db_/] }
=> {:db_name => value, .... }
This returns me nicely all the k,v that I need. But I also want to strip the db_ from the keynames so that it returns me
=> {:name => value, ....}
I tried something like
Project.config.to_h.select{ |k,v| k[/db_/] }.each_key { |k| k.to_s.gsub(/db_/) }
But it returns the same hash like the above example. Any idea or suggestions to get this as a smooth one or two liner?
inject to the rescue (assuming the db_ is in the beginning):
Project.config.to_h.inject({}) do | a, (k, v) |
k =~ /\Adb_/ ? a.update($' => v) : a
end
inject can be used to iterate through an enumerable data structure and build any kind of output in the course.
$' is a magic variable containing the portion of the string after a successful regexp match.
So let's break down your solution and see what's wrong with it:
Project.config.to_h.select{ |k,v| k[/db_/] }.each_key { |k| k.to_s.gsub(/db_/) }
So:
k.to_s.gsub(/db_/)
is creating a new object (and not mutating the keys like you would expect), in this case an enumerator, when you don't pass gsub a second argument it returns an enumerator, so let's assume you did:
k.to_s.gsub(/db_/, "")
This would be creating new objects and not mutating the keys in the hash(to_s would create a new string object, and gsub doesn't mutate, instead creates a new object, gsub! is the mutant version)
Now let's assume the keys were strings and you could even mutate them (with gsub!), ruby would not let you mutate them in place, else ruby hash would be broken, when a string is used as a key it is frozen, see http://ruby-doc.org/core-2.2.2/Hash.html#5B-5D-3D-method
key should not have its value changed while it is in use as a key (an unfrozen String passed as a key will be duplicated and frozen).
So with this knowledge how would I implement this:
Project.config.to_h.each_with_object({}) do |(k,v), hsh|
next unless k[/db_/] # skip every key that doesn't match the regex (if you want the keys to not have part of them stripped then you can use an if/else along with the next line)
hsh[k.to_s.sub(/db_/, "")] = v
end

Ruby regex selecting multiple words at the same time

I have a hash that I am using regex on to select what key/value pairs I want. Here is the method I have written:
def extract_gender_race_totals(gender, race)
totals = #data.select {|k,v| k.to_s.match(/(#{gender})(#{race})/)}
temp = 0
totals.each {|key, value| temp += value}
temp
end
the hash looks like this:
#data = {
:number_of_african_male_senior_managers=>2,
:number_of_coloured_male_senior_managers=>0,
:number_of_indian_male_senior_managers=>0,
:number_of_white_male_senior_managers=>0,
:number_of_african_female_senior_managers=>0,
:number_of_coloured_female_senior_managers=>0,
:number_of_indian_female_senior_managers=>0,
:number_of_white_female_senior_managers=>0,
:number_of_african_male_middle_managers=>2,
:number_of_coloured_male_middle_managers=>0,
:number_of_indian_male_middle_managers=>0,
:number_of_white_male_middle_managers=>0,
:number_of_african_female_middle_managers=>0,
:number_of_coloured_female_middle_managers=>0,
:number_of_indian_female_middle_managers=>0,
:number_of_white_female_middle_managers=>0,
:number_of_african_male_junior_managers=>0,
:number_of_coloured_male_junior_managers=>0,
:number_of_indian_male_junior_managers=>0,
:number_of_white_male_junior_managers=>0,
:number_of_african_female_junior_managers=>0,
:number_of_coloured_female_junior_managers=>0,
:number_of_indian_female_junior_managers=>0,
:number_of_white_female_junior_managers=>0
}
but it's re-populated with data after a SQL Query.
I would like to make it so that the key must contain both the race and the gender in order for it to return something. Otherwise it must return 0. Is this right or is the regex syntax off?
It's returning 0 for all, which it shouldn't.
So the example would be
%td.total_cell= #ee_demographics_presenter.extract_gender_race_totals("male","african")
This would return 4, there are 4 African, male managers.
Try something like this.
def extract_gender_race_totals(gender, race)
#data.select{|k, v| k.to_s.match(/#{race}_#{gender}/)}.values.reduce(:+)
end
extract_gender_race_totals("male", "african")
# => 4
gmalete's answer gives an elegant solution, but here is just an explanation of why your regexp isn't quite right. If you corrected the regexp I think your approach would work, it just isn't as idiomatic Ruby.
/(#{gender})(#{race})/ won't match number_of_african_male_senior_managers for 2 reasons:
1) the race comes before the gender in the hash key and 2) there is an underscore in the hash key that needs to be in the regexp. e.g.
/(#{race})_(#{gender})/
would work, but the parentheses aren't needed so this can be simplified to
/#{race}_#{gender}/
Rather than having specific methods to query pieces of your keys (i.e. "gender_race"), you could make a general method to query any attribute in any order:
def extract_totals(*keywords)
keywords.inject(#data) { |memo, keyword| memo.select { |k, v| k.to_s =~ /_#{keyword}(?:_|\b)/ } }.values.reduce(:+)
end
Usage:
extract_totals("senior")
extract_totals("male", "african")
extract_totals("managers") # maybe you'll have _employees later...
# etc.
Not exactly what you asked for, but maybe it will help.

A string (thats looks like an array) into an array Ruby

I have an output from an API that look like this... (its a string)
[[2121212,212121,asd],[2323232,23232323,qasdasd]]
Its a string - not an array. I want to convert it to an array and then extract the first two elements in each array in the nested array to:
[2121212,212121],[2323232,23232323]
What's the best way to do this ruby? I could use regexp and extract - but basically the string is already an array, however the class is a string.
I tried
array.push(response)
but that just put the string in to the array as one element. I guess what would be nice is a to_array method
You will need to use regular expression anyway if not eval (shrudder...), this is the shortest one
str = "[[2121212,212121,asd],[2323232,23232323,qasdasd],[2424242,24242424,qasdasd]]"
p str.scan(/(\d+),(\d+)/)
=>[["2121212", "212121"], ["2323232", "23232323"], ["2424242", "24242424"]]
Assuming this is a JSON response (and if so, it is badly malformed and you should talk to the people that are responsible for this) you could write something like:
require 'json'
input= '[[2121212,212121,Asd],[2323232,23232323,qasdasd]]'
input.gsub!(/([A-Za-z ]+)/,'"\1"')
json = JSON.parse input
output = json.map{|x| x[0...2]}
p output
this prints
[[2121212, 212121], [2323232, 23232323]]
Using eval is very bad but I have no other easy option.
test_str = "[[2121212,212121,asd],[2323232,23232323,qasdasd]]"
test_str.gsub!(/([a-z]+)/) do
"'#{$1}'"
end
=> "[[2121212,212121,'asd'],[2323232,23232323,'qasdasd']]"
test_array = eval(test_str)
=> [[2121212, 212121, "asd"], [2323232, 23232323, "qasdasd"]]
test_array.each do |element|
element.delete(element.last)
end
=> [[2121212, 212121], [2323232, 23232323]]

ruby - need help understanding this inject

I'd like to understand how the following code works:
def url
#url ||= {
"basename" => self.basename,
"output_ext" => self.output_ext,
}.inject("/:basename/") { |result, token|
result.gsub(/:#{token.first}/, token.last)
}.gsub(/\/\//, "/")
end
I know what it does; somehow it returns the url corresponding to a file located o a dir on a server. So it returns strings similar to this: /path/to/my/file.html
I understand that if #url already has a value, it will be returned and the right ||= will be discarded. I also understand that this begins creating a hash of two elements.
I also think I understand the last gsub; it replaces backslashes by slashes (to cope with windows servers, I guess).
What amazes me is the inject part. I'm not able to understand it. I have used inject before, but this one is too much for me. I don't see how this be done with an each, since I don't understand what it does.
I modified the original function slightly for this question; the original comes from this jekyll file.
Cheers!
foo.inject(bar) {|result, x| f(result,x) }
Can always be written as:
result = bar
foo.each {|x| result = f(result, x)}
result
So for your case, the version with each would look like this:
result = "/:basename/"
{
"basename" => self.basename,
"output_ext" => self.output_ext,
}.each {|token|
result = result.gsub(/:#{token.first}/, token.last)
}
result
Meaning: for all key-value-pairs in the hash, each occurrence of the key in the "/:basename/" is replaced with the value.
Perhaps splitting the code and tweaking a little helps
options = { "basename" => self.basename, "output_ext" => self.output_ext }
options.inject("/:basename") do |result, key_and_kalue|
# Iterating over the hash yields an array of two elements, which I called key_and_value
result.gsub(":#{key_and_value[0]}", key_and_value[1])
end.gsub!(//\/\/, '/')
Basically, the inject code is iterating over all your options and replacing for the actual value wherever it sees a ":key"

Resources