sophie8
sophie8

Reputation: 17

Is there a way in Scala to convert Seq[(String, String)] To Seq[String]?

I'm new to Scala and really coding in general so sorry if this question is a bit basic but would love some help!

I currently have a function which yields Seq[(String, String)] which I would then like to convert to Seq[String].

Depending on the iteration of the function, the dimension of the vector will be different so some outcomes are Seq[(String, String)], others are Seq[(String, String, String)] etc. So ideally I could use the same function for each output.

I have tried using .flatten, and split[","] and am not sure what other methods I could use.

Fr one sample value, output of the function looks like this:

res17: Seq[(String, String)] = Vector((u, v),(w, x))

The ideal result would be Vector(u,v,w,x)

Upvotes: 0

Views: 1203

Answers (2)

Mario Galic
Mario Galic

Reputation: 48420

Consider shapeless approach which provides a way of abstracting over arity in type-safe manner:

import shapeless._
import shapeless.ops.hlist
import syntax.std.tuple._

def flattenTupleN[P <: Product, L <: HList](
  ps: List[P]
)(implicit gen: Generic.Aux[P, L],
  toT: hlist.ToTraversable.Aux[L, List, String]
): List[String] = {
  ps.flatMap(p => gen.to(p).toList) 
}

val t1: List[(String, String)] = List(("u", "v"), ("w", "x"))
val t2: List[(String, String, String)] = List(("a", "b", "c"), ("d", "e", "f"))
val t3: List[(Int, Double, String)] = List((42, 3.14, "Picard"))

flattenTupleN(t1)
flattenTupleN(t2)
// flattenTupleN(t3) // compile-time error

which outputs

res0: List[String] = List(u, v, w, x)
res1: List[String] = List(a, b, c, d, e, f)

Here is an unsafe but out-of-the-box approach:

def flattenTupleNUnsafe(ps: List[Product]): List[String] =
  ps.flatMap(_.productIterator.map(_.toString))

flattenTupleNUnsafe(t1)
flattenTupleNUnsafe(t2)
flattenTupleNUnsafe(t3) // compiles OK but is a bad idea!

which outputs

res2: List[String] = List(u, v, w, x)
res3: List[String] = List(a, b, c, d, e, f)
res4: List[String] = List(42, 3.14, Picard)

Note how due to loss of type-safety flattenTupleNUnsafe(t3) compiles just fine and introduces a logic error in the program.

Upvotes: 1

Allen Han
Allen Han

Reputation: 1163

I agree with those people who are asking for more information about your use case. Without knowing more about the data you are trying to transform, it is difficult to recommend a concise solution that addresses your needs.

Please consider what I have to say in a provisional light, as I do not know enough about what you are trying to do.

You say that the tuple you are outputting can vary. Have you considered converting everything from Tuple to Vector?

So instead of

res17: Vector[(String, String)] = Vector((u, v),(w, x))

use

res17: Vector[Vector[String]] = Vector(Vector(u, v),Vector(w, x))

You can then easily transform from the Vector of Vector to a single Vector with a call to flatMap or flatten, which based on your question, it sounds like you already know how to do.

Another impression I get from reading your question is that if you want to keep a fixed length argument list that you transform from that argument list to a Vector, you might want to look at case classes instead of Tuple.

So instead of Tuple2 or Tuple3, define an inheritance hierarchy that allows the compiler to type check your program.

Something like this:

trait MyData

case class 2MemberData(data1: String, data2: String) extends MyData

case class 3MemberData(data1: String, data2: String, data3: String) extends MyData

case class 4MemberData(data1: String, data2: String, data3: String, data4: String) extends My Data

That way your function can output a value of type Vector[MyData] that you then flatten using pattern matching. So something like

def processData: Vector[MyData]

def cleanUp(input: Vector[MyData]): Vector[String]

where cleanUp is implemented as follows:

def cleanUp(input: Vector[MyData]): Vector[String] = {
  input.flatMap{ d =>
    d match {
      case 2MemberData(data1, data2) => Vector(data1, data2)
      case 3MemberData(data1, data2, data3) => Vector(data1, data2, data3)
      case 4MemberData(data1, data2, data3, data4) => Vector(data1, data2, data3, data4)
    }
  }
}

I'm just throwing ideas out there and don't know if what I am saying is helpful or not. It really depends on what the surrounding code looks like. If you have any questions, feel free to ask.

Upvotes: 0

Related Questions