154

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?

Donald Duck
  • 8,409
  • 22
  • 75
  • 99
Flackou
  • 3,631
  • 4
  • 27
  • 24
  • 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 :-)) – Vojto Jul 10 '10 at 05:53

10 Answers10

187

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"]}
Arup Rakshit
  • 116,827
  • 30
  • 260
  • 317
Luqman
  • 3,070
  • 1
  • 19
  • 9
  • 2
    OK, that was the one I missed! It's perfect when used with URI.parse : CGI.parse(URI.parse("http://www.example.com/something?param1=value1&param2=value2&param3=value3").query) returns the desired Hash. Thanks for your help. – Flackou Mar 23 '10 at 14:40
  • 97
    For clarity, @Flackou wants this: `CGI.parse(URI.parse(url).query)` – glenn jackman Mar 23 '10 at 15:12
  • 1
    I haven't tested this, but the first key listed, containing the full url, seems really wrong. – Levi Mar 26 '10 at 04:14
  • 4
    I actually had to use this: `CGI::parse(URI::parse(url).query)` – benathon Apr 28 '13 at 08:15
  • 1
    This solution won't work properly with arrays, like `a[]=1&a[]=2` will be parsed as `{"a[]"=>["1", "2"]}` whereas [Arthur's solution](https://stackoverflow.com/a/3218018/895789) will parse `{"a"=>["1", "2"]}` – Alter Lagos Oct 06 '22 at 03:55
178

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"}
Arthur Gunn
  • 1,781
  • 1
  • 10
  • 2
  • Much more lightweight than mocking a Rack request – Gareth Aug 04 '11 at 22:18
  • 2
    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: https://github.com/rack/rack/blob/master/lib/rack/utils.rb – Levi Dec 03 '11 at 15:57
  • 1
    This works well but does not work for checkboxes with the same name: `param1=value1&param1=value2`. The second value trumps the first. – B Seven Dec 10 '15 at 04:52
  • 3
    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`). – zelanix Apr 26 '16 at 06:40
  • This method crashes sometimes because of encoding issues. I found the `CGI:parse` method to be more stable (see answer above) – Yossi Shasho Mar 01 '17 at 15:49
113

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" } 
rtdp
  • 2,067
  • 1
  • 17
  • 32
  • 7
    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. – jackocnr Nov 15 '13 at 23:25
  • 4
    As I commented above, for anyone looking for the inverse, it's `Rack::Utils.build_query(params)`. – zelanix Apr 26 '16 at 06:41
  • 2
    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"}. – Raels Jul 20 '16 at 03:59
  • 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. – wbharding Mar 21 '21 at 16:07
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", ...] }
alup
  • 2,961
  • 1
  • 21
  • 12
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.

Arup Rakshit
  • 116,827
  • 30
  • 260
  • 317
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)

Yarin
  • 173,523
  • 149
  • 402
  • 512
  • 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 `url`s 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. – Kelsey Hannan Aug 24 '21 at 10:23
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"} 
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

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": "john@email.com"}
Kelsey Hannan
  • 2,857
  • 2
  • 30
  • 46
-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.

kafuchau
  • 5,573
  • 7
  • 33
  • 38
  • OK, I knew that, it works well in the controller with the requested URL, but how to do that for others arbitrary URLs? – Flackou Mar 23 '10 at 14:31