8

I have a query string in this form:

val query = "key1=val1&key2=val2&key3=val3

I want to create a map with the above key/value pairs. So far I'm doing it like this:

//creating an iterator with 2 values in each group. Each index consists of a key/value pair
val pairs = query.split("&|=").grouped(2)

//inserting the key/value pairs into a map
val map = pairs.map { case Array(k, v) => k -> v }.toMap

Are there any problems with doing it like I do? If so, is there some library I could use to do it?

Suraj Rao
  • 29,388
  • 11
  • 94
  • 103
Michael
  • 193
  • 3
  • 11
  • Check this post: http://stackoverflow.com/questions/13592236/parse-a-uri-string-into-name-value-collection They mention different approaches doing the same in Java or using Java libraries. So there should be no problem to use them in Scala. – matfax Mar 15 '17 at 14:37
  • 2
    That looks good and simpler that using an external library. – nmat Mar 15 '17 at 16:50

2 Answers2

3

Here is an approach using the URLEncodedUtils:

import java.net.URI

import org.apache.http.client.utils.URLEncodedUtils
import org.apache.http.{NameValuePair => ApacheNameValuePair}

import scala.collection.JavaConverters._
import scala.collection.immutable.Seq

object GetEncodingTest extends App {
  val url = "?one=1&two=2&three=3&three=3a"
  val params = URLEncodedUtils.parse(new URI(url), "UTF_8")

  val convertedParams: Seq[ApacheNameValuePair] = collection.immutable.Seq(params.asScala: _*)
  val scalaParams: Seq[(String, String)] = convertedParams.map(pair => pair.getName -> pair.getValue)
  val paramsMap: Map[String, String] = scalaParams.toMap
  paramsMap.foreach(println)
}
matfax
  • 634
  • 11
  • 17
  • Hi. This works with the full url string, but the string i need to convert contains the query string only. That is eg. like "key1=value1&key2=value2" and nothing else. Also the parse methods seems to be deprecated? – Michael Mar 16 '17 at 07:50
  • 1
    Well, is solved the little issues with the solution. If i prepend a '?' to my string, the parser can work with it. Furthermore if i use StandardCharsets.UTF_8 as a parameter in the parse method instead of "UTF-8" the non-deprecated method is called. Ty :) – Michael Mar 16 '17 at 14:13
  • 1
    URLEncodedUtils is part of apache's HttpClient (https://mvnrepository.com/artifact/org.apache.httpcomponents/httpclient) ```libraryDependencies += "org.apache.httpcomponents" % "httpclient" % "4.5.11" ``` – Jake Mar 07 '20 at 18:50
1

Assuming the query string you are working with is as simple as you showed, the use of grouped(2) is a great insight and gives a pretty elegant looking solution.

The next step from where you're at is to use the under-documented Array::toMap method:

val qs = "key=value&foo=bar"
qs.split("&|=")              // Array(key, value, foo, bar)
  .grouped(2)                // <iterator>
  .map(a => (a(0), a(1)))    // <iterator>
  .toMap                     // Map(key -> value, foo -> bar)

grouped(2) returns an Iterator[Array[String]], that's a little harder to follow because iterators don't serialize nicely on the Scala console.

Here's the same result, but a bit more step-by-step:

val qs = "key=value&foo=bar"
qs.split("&")                                      // Array(key=value, foo=bar)
  .map(kv => (kv.split("=")(0), kv.split("=")(1))) // Array((key,value), (foo,bar))
  .toMap                                           // Map(key -> value, foo -> bar)

If you want a more general solution for HTTP query strings, consider using a library for URL parsing.

yegeniy
  • 1,272
  • 13
  • 28