1
\$\begingroup\$

My current code:

val headers : Map[String, Seq[String]] = ...
//NOTE:  ("key" -> Seq()) is  valid entry
headers.toList flatMap {x => x._2 map { y => (x._1 -> y) }}

This feels clumsy...is there a better way to do this?

Goal

I have a Map[String, Seq[String]] representing all the headers in an HTTP request. That needs to be translated to a Seq[(String, String)] in order to be passed to a method that expects that headers to be stored in that format.

The code is supposed to convert to this format. The pair "something" -> Seq("val1", "val2", "val3") would be translated to 3 (String,String)'s. "something" -> Seq() would be translated to 0.

Concern

I'm still getting the hang of some aspects of the functional approach. In a lot of cases, this one included, it feels like I'm building hard-to read, overly complex code. I get that it's just a tolist + two maps, but this seems way less clear than some of the imperative alternatives, for example:

val ret = mutable.Seq[(String,String]()
for (kv <- headers; value <- kv._2) ret :+ (kv._1, value)

or even:

Set[Tuple2[String,String]] ret = new HashSet[Tuple2[String,String]]()
for (key : headers.keySet) {
  for (value : headers.get(key) {
     ret.add(new Tuple2(key, value))
  } 
}  
\$\endgroup\$
2
  • \$\begingroup\$ Please provide an explanation of what your code is trying to accomplish, this enables more people to understand your code and you will get answers faster. \$\endgroup\$
    – skiwi
    Commented May 20, 2014 at 20:22
  • \$\begingroup\$ Updated. Basic question is how to transform a Map[String, Seq[String]] to a Seq[(String,String)]. The resulting Seq will contain NUMBER_OF_KEYS * SUM(ELEMENTS_IN_VALUE). The broader goal of the question is just to get an idea of what The Official Clean Functional Way (tm) to approach these problems in Scala is. \$\endgroup\$ Commented May 20, 2014 at 20:54

1 Answer 1

4
\$\begingroup\$

I don't see how the functional code looks clumsy or difficult to read. If you have problems reading functional code, than don't write it - the imperative code is entirely fine, as long as the side effects remain local to a single function.

A lot of readability can be gained by giving your intermediate values useful names and to apply a different formatting:

headers.toSeq flatMap {
  case (parameter, values) =>
     values map (parameter -> _)
}

No need to squash everything into a single line or to use tuples ugly _X accessors.

Another way to write your code is:

for ((parameter, values) <- m.toSeq; v <- values)
  yield parameter -> v

which looks very similar to the imperative solution but is exactly the same as the code before.

Write code that belongs to your skill level. If you don't feel fine with shorter and more functional code, then let it longer and more imperative. Improve your skills during the next months and refactor the code once you come back and see the need for it.

\$\endgroup\$
1
  • \$\begingroup\$ Re: not being comfortable reading functional code, etc. Yeah that's the point of doing this. There's a strong technical case for using both a more functional style and the scala language specifically for some upcoming projects. IMO the best way to learn is to code. I'm at the phase where I understand a lot of the theory, but not the practice. For example: I hate the ._N tuple properties, but never really thought to use pattern matching to alias them. I understand why it works, but it just wasn't something that occurred to me. This is the kind of feedback I wanted, thanks! \$\endgroup\$ Commented May 20, 2014 at 21:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.