ruby 48 lines · 9 steps

Parsing nested query strings in Ruby

A parser that turns a raw query string into a nested hash, handling bracket syntax like a[b][]=1.

Explained by highlit
1class QueryParser
2 def self.parse(query_string)
3 new(query_string).parse
4 end
5 
6 def initialize(query_string)
7 @query_string = query_string.to_s.sub(/\A\?/, "")
8 end
9 
10 def parse
11 params = {}
12 @query_string.split("&").each do |pair|
13 next if pair.empty?
14 
15 key, value = pair.split("=", 2)
16 assign(params, tokenize(unescape(key)), unescape(value))
17 end
18 params
19 end
20 
21 private
22 
23 def tokenize(key)
24 name, rest = key.split("[", 2)
25 keys = [name]
26 keys.concat(rest.scan(/[^\[\]]*/).reject(&:empty?)) if rest
27 keys << "" if rest&.end_with?("[]")
28 keys
29 end
30 
31 def assign(container, keys, value)
32 head, *tail = keys
33 
34 if tail.empty?
35 container[head] = value
36 elsif tail == [""]
37 (container[head] ||= []) << value
38 else
39 nested = container[head] ||= {}
40 assign(nested, tail, value)
41 end
42 end
43 
44 def unescape(str)
45 return if str.nil?
46 CGI.unescape(str)
47 end
48end
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1A convenience class method plus a per-instance method keeps stateful parsing clean and reusable.
  2. 2Splitting a key into path segments turns flat query strings into recursively-built nested structures.
  3. 3Distinguishing scalar, array, and hash cases lets one assign routine handle arbitrarily deep nesting.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Parsing nested query strings in Ruby — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code