How to extract URL parameters from a URL with Ruby or Rails?
Asked Answered
R

11

155

I have some URLs, like

http://www.example.com/something?param1=value1&param2=value2&param3=value3

and I would like to extract the parameters from these URLs and get them in a Hash. Obviously, I could use regular expressions, but I was just wondering if there was easier ways to do that with Ruby or Rails. I haven't found anything in the Ruby module URI but perhaps I missed something.

In fact, I need a method that would do that:

extract_parameters_from_url("http://www.example.com/something?param1=value1&param2=value2&param3=value3")
#=> {:param1 => 'value1', :param2 => 'value2', :param3 => 'value3'}

Would you have some advices?

Rosado answered 23/3, 2010 at 14:0 Comment(1)
Hash[*string.split('&').collect{|i|i.split('=')}.flatten] This would work too, but it's probably the worst option for this case. But still you might find this snippet interesting. (Posting as comment since I don't consider this an answer :-))Phenolic
S
189

I think you want to turn any given URL string into a HASH?

You can try http://www.ruby-doc.org/stdlib/libdoc/cgi/rdoc/classes/CGI.html#M000075

require 'cgi'

CGI::parse('param1=value1&param2=value2&param3=value3')

returns

{"param1"=>["value1"], "param2"=>["value2"], "param3"=>["value3"]}
Substantialize answered 23/3, 2010 at 14:20 Comment(5)
OK, that was the one I missed! It's perfect when used with URI.parse : CGI.parse(URI.parse("example.com/…) returns the desired Hash. Thanks for your help.Rosado
For clarity, @Rosado wants this: CGI.parse(URI.parse(url).query)Cotinga
I haven't tested this, but the first key listed, containing the full url, seems really wrong.Goren
I actually had to use this: CGI::parse(URI::parse(url).query)Degenerate
This solution won't work properly with arrays, like a[]=1&a[]=2 will be parsed as {"a[]"=>["1", "2"]} whereas Arthur's solution will parse {"a"=>["1", "2"]}Egocentrism
Q
180

I found myself needing the same thing for a recent project. Building on Levi's solution, here's a cleaner and faster method:

Rack::Utils.parse_nested_query 'param1=value1&param2=value2&param3=value3'
# => {"param1"=>"value1", "param2"=>"value2", "param3"=>"value3"}
Qualifier answered 10/7, 2010 at 5:46 Comment(5)
Much more lightweight than mocking a Rack requestOnto
Good find. If you have simple params (non-nested) and are performance sensitive, Rack::Utils.parse_query might be of interest. The code is worth reading: github.com/rack/rack/blob/master/lib/rack/utils.rbGoren
This works well but does not work for checkboxes with the same name: param1=value1&param1=value2. The second value trumps the first.Dormancy
For anyone looking for the inverse, it's Rack::Utils.build_nested_query(params) (or Rack::Utils.build_query(params) if parsed with Rack::Utils.parse_query).Robalo
This method crashes sometimes because of encoding issues. I found the CGI:parse method to be more stable (see answer above)Fie
S
114

Just Improved with Levi answer above -

Rack::Utils.parse_query URI("http://example.com?par=hello&par2=bye").query

For a string like above url, it will return

{ "par" => "hello", "par2" => "bye" } 
Seiter answered 22/2, 2012 at 15:56 Comment(4)
great answer. simple, processes the full URL like the op asks, and the result values are strings instead of arrays, like in the other answers. thanks.Burp
As I commented above, for anyone looking for the inverse, it's Rack::Utils.build_query(params).Robalo
Beware - not quite the inverse, at least not in Ruby 1.8.7 / Rails 2.3. A query string of foo[]=1&foo[]=2 is correctly parsed as { "foo" =>["1","2"] }, but build_query turns that into "foo=1&foo=2", which when parsed again yields { "foo"=>"2"}.Nee
Took me a minute to realize that due to lack of wrapping params, this answer isn't readily extended. Rack::Utils.parse_query(URI("http://example.com?par=hello&par2=bye").query) yields a hash that can be further modified.Cacogenics
P
55

For a pure Ruby solution combine URI.parse with CGI.parse (this can be used even if Rails/Rack etc. are not required):

CGI.parse(URI.parse(url).query) 
# =>  {"name1" => ["value1"], "name2" => ["value1", "value2", ...] }
Prepossess answered 7/6, 2012 at 10:25 Comment(1)
This is elegant.Grumpy
S
44

There more than one ways, to solve your problem. Others has shown you the some tricks. I know another trick. Here is my try :-

require 'uri'
url = "http://www.example.com/something?param1=value1&param2=value2&param3=value3"
uri = URI(url)
# => #<URI::HTTP:0x89e4898 URL:http://www.example.com/something?param1=value1&param2=value2&param3=value3>
URI::decode_www_form(uri.query).to_h # if you are in 2.1 or later version of Ruby
# => {"param1"=>"value1", "param2"=>"value2", "param3"=>"value3"}
Hash[URI::decode_www_form(uri.query)] # if you are below 2.1 version of Ruby
# => {"param1"=>"value1", "param2"=>"value2", "param3"=>"value3"}

Read the method docomentation of ::decode_www_form.

Scandium answered 1/9, 2014 at 2:48 Comment(0)
M
16

Check out the addressable gem - a popular replacement for Ruby's URI module that makes query parsing easy:

require "addressable/uri"
uri = Addressable::URI.parse("http://www.example.com/something?param1=value1&param2=value2&param3=value3")
uri.query_values['param1']
=> 'value1'

(It also apparently handles param encoding/decoding, unlike URI)

Minim answered 2/1, 2015 at 15:59 Comment(1)
Sadly it looks this gem, like the URI library, has difficulty parsing query parameters in situations where the URL is weird. I.e. both fail to find query parameters when urls are odd, e.g. this: http://localhost:4300/webapp/foo/#//controller/action? Leaving me to use a hack like: Rack::Utils.parse_nested_query(url.split("?").last) to get the query parameters for parsing.Ard
B
8

Using CGI might be an outdated approach with Ruby 2.7/3.

Here's a neat way to do this with URI:

uri = URI.parse 'https://duckduckgo.com/?q=ruby+programming+language'
params = Hash[URI.decode_www_form uri.query]
# => {"q"=>"ruby programming language"} 
Biltong answered 22/10, 2021 at 17:45 Comment(1)
Look, no additional libraries needed 👍Giesser
N
2

you can also use this method


require 'uri'
require 'cgi'
uri = URI("https://example.com/?query=1&q=2&query=5")
a = CGI::parse(uri.query)
puts a                   #=> {"query"=>["1", "5"], "q"=>["2"]}
puts a["query"].to_s     #=> ["1", "5"]
puts a["query"][0]       #=>  1
puts a["query"][1]       #=>  5
puts a["q"][0]           #=>  2


its safe and much easier

Nedranedrah answered 28/5, 2021 at 20:46 Comment(0)
A
2

Sadly both the URI and addressable libraries break when attempting to extract query params from buggy URLs. E.g. this breaks both:

http://localhost:4300/webapp/foo/#//controller/action?account=001-001-111&email=john%40email.com

Building on Arthur / Levi's solution, with url.split("?").try(:last) you can grab just the query param portion of the URL, and use Rack::Utils.parse_nested_query to parse that string of parameters into a hash.

Or in full:

Rack::Utils.parse_nested_query(url.split("?").try(:last))

returning in my example:

{"account": "001-001-111", "email": "[email protected]"}
Ard answered 24/8, 2021 at 10:51 Comment(0)
R
0

The above answers are very well written and they serve the purpose, however I tried some query manipulation and wanted to share here. So, here is my take:

   URI("http://example.com?par=hello&par2=bye").query.split('&').map { |param| {query_param: param.split('=')[0] , value: param.split('=')[1]}  }

It serves the purpose and return an array of objects for each query param. Having said that, please keep in mind the COC principle of ROR.

Ralph answered 12/4, 2024 at 13:47 Comment(0)
V
-4

In your Controller, you should be able to access a dictionary (hash) called params. So, if you know what the names of each query parameter is, then just do params[:param1] to access it... If you don't know what the names of the parameters are, you could traverse the dictionary and get the keys.

Some simple examples here.

Virgy answered 23/3, 2010 at 14:7 Comment(1)
OK, I knew that, it works well in the controller with the requested URL, but how to do that for others arbitrary URLs?Rosado

© 2022 - 2025 — McMap. All rights reserved.