Passing original string PLUS matches when using gsub and block? - ruby

Rails fans are familiar with params[:terms] or a hash of 'things' passed to the controller collected form the url. E.g.:
params
=> {"term"=>"Warren Buffet",
"controller"=>"search",
"format"=>"json",
"action"=>"index"}
If I want to use "Warren Buffet", "Warren" and "Buffet" in the code below, does anyone know which method I should be using instead? gsub is close, but it takes each match and not the original string too. Unless I'm doing it wrong, which is totally possible:
#potential_investors = []
params[:term].gsub(/(\w{1,})/) do |term|
#potential_investors &lt&lt User.where(:investor => true)
.order('first_name ASC, last_name ASC')
.search_potential_investors(term)
end
Thoughts?

How about:
s = "Filthy Rich"
s.split(" ").push(s)
>> ["Filthy", "Rich", "Filthy Rich"]
Or with scan if you prefer to use the regexp instead:
s.scan(/\w+/).push(s)
>> ["Filthy", "Rich", "Filthy Rich"]

params["term"].gsub(/(\w{1,})/)
returns an enumerator. You could convert it to an array and append the original term to it:
ary = params["term"].gsub(/(\w{1,})/).to_a + [params["term"]]
then process it:
ary.each do |term|
...

Related

How to return decoded string based on encoded string Ruby

For x = '4[kl]', the output should be 'klklklkl'
For s = '3[k2[b]]', the output should be 'kbbkbbkbb'
I've been working with the regex .scan(/(\[(?:\[??[^\[]*?\]))/) to find nested brackets
s = '3[k2[b]]'
s.scan(/(\[(?:\[??[^\[]*?\]))/)
=> [["[b]"]]
s = '3[k2[b]]'
nil while s.sub!(/(\d+)\[([^\[\]]*)\]/){$2 * $1.to_i}
s #=> "kbbkbbkbb"
I am not sure that might be done within one pass, but it’s easy to achieve with a recursion:
%w|4[kl] 3[k2[b]]|.map do |s|
loop do
break s if s.gsub!(/(\d+)\[([^\[\]]+)\]/) { $2 * $1.to_i }.nil?
end
end
#⇒ ["klklklkl", "kbbkbbkbb"]
We use here that String#gsub! returns nil on no matches, terminating the whole loop.
Please note, that the code above is destructing, that said it mutates an input. If you want the original string to stay intact, dup it upfront.

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]]

Why does Array.to_s return brackets?

For an array, when I type:
puts array[0]
==> text
Yet when I type
puts array[0].to_s
==> ["text"]
Why the brackets and quotes? What am I missing?
ADDENDUM: my code looks like this
page = open(url) {|f| f.read }
page_array = page.scan(/regex/) #pulls partial urls into an array
partial_url = page_array[0].to_s
full_url = base_url + partial_url #adds each partial url to a consistent base_url
puts full_url
what I'm getting looks like:
http://www.stackoverflow/["questions"]
This print the array as is without brackets
array.join(", ")
to_s is just an alias to inspect for the Array class.
Not that this means a lot other than instead of expecting array.to_s to return a string it's actually returning array.inspect which, based on the name of the method, isn't really what you are looking for.
If you want just the "guts" try:
page_array.join
If there are multiple elements to the array:
page_array.join(" ")
This will make:
page_array = ["test","this","function"]
return:
"test this function"
What "to_s" on an Array returns, depends on the version of Ruby you are using as mentioned above. 1.9.X returns:
"[\"test\"]"
You need to show us the regex to really fix this properly, but this will do it:
Replace this
partial_url = page_array[0].to_s
with this
partial_url = page_array[0][0]
This doesn't necessarily fix why you are getting a doubled-up array, but you can flatten it and then call the first element like this.
page_array = page.scan(/regex/).flatten
Flattening takes out stacked arrays and creates one level, so if you had [1,2,[3,[4,5,6]]] and called flatten on it, you would get [1,2,3,4,5,6]
It is also more robust than doing array[0][0], because, if you had more than two arrays nested in the first element, you would run into the same issue.
Iain is correct though, without seeing the regex, we can't suss out the root cause.

parsing in 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') }

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