问题
I want to split a string delimited by commas and use the result as either a Seq
or a Set
:
def splitByComma(commaDelimited: String): Array[String]
= commaDelimited.trim.split(',')
def splitByCommaAsSet(commaDelimited: String): Set[String]
= splitByComma(commaDelimited).toSet
def splitByCommaAsSeq(commaDelimited: String): Seq[String]
= splitByComma(commaDelimited).toSeq
val foods = "sushi,taco,burrito"
val foodSet = splitByCommaAsSet(foods)
// foodSet: scala.collection.immutable.Set[String] = Set(sushi, taco, burrito)
val foodSeq = splitByCommaAsSeq(foods)
// foodSeq: Seq[String] = List(sushi, taco, burrito)
However, this is quite repetitive. Ideally, I would want to have something like genericSplitByComma[Set](foods)
that just returns a Set
when I pass Set
in, and returns a Seq
when I pass Seq
.
回答1:
@KrzysztofAtłasik's answer works great for Scala 2.12
.
This is a solution for 2.13
. (Not completely sure if this is the best way).
import scala.collection.Factory
import scala.language.higherKinds
def splitByComma[C[_]](commaDelimited: String)(implicit f: Factory[String, C[String]]): C[String] =
f.fromSpecific(commaDelimited.split(","))
// Or, as Dmytro stated, which I have to agree looks better.
commaDelimited.split(",").to(f)
Which you can use like this:
splitByComma[Array]("hello,world!")
// res: Array[String] = Array(hello, world!)
splitByComma[Set]("hello,world!")
// res: Set[String] = Set(hello, world!)
splitByComma[List]("hello,world!")
// res: List[String] = List(hello, world!)
回答2:
There's a method in Scala called to
which can transform arbitrary collection to another as long as there is typeclass called CanBuildFrom
in scope.
import scala.collection.generic.CanBuildFrom
import scala.languageFeature.higherKinds
def genericSplitByComma[S[_]](s: String)(implicit cbf: CanBuildFrom[Nothing, String, S[String]]): S[String] = {
s.split(",").to[S]
}
genericSplitByComma[Set]("Hello, hello") //Set(Hello, hello)
genericSplitByComma[List]("Hello, hello") //List(Hello, hello)
genericSplitByComma[Array]("Hello, hello") //Array(hello, world!)
We don't need to constrain S[_]
because this function won't compile if there is no suitable CanBuildFrom
in scope. For example, this will fail:
genericSplitByComma[Option]("Hello, hello")
Below will also fail because our type constructor S[_]
accepts only one type argument and the map expects two:
genericSplitByComma[Map]("Hello, hello")
As Luis Miguel Mejía Suárez and Dmytro Mitin noticed, there was major refactor in collections in just-released Scala 2.13, so it will work up to Scala 2.12.
回答3:
There's a simple workaround for this. Not exactly the requested syntax but just as concise and it should be 2.13 compatible.
def simpleSplitByComma(coll :Iterable[String]) =
coll.flatMap(_.trim.split(","))
simpleSplitByComma(Set("hello,world")) //res0: Set(hello, world)
simpleSplitByComma(Seq("bellow,world")) //res1: List(bellow, world)
simpleSplitByComma(Array("fellow,old")) //res2: ArrayBuffer(fellow, old)
simpleSplitByComma(Stream("end,of,the,world")) //res3: Stream(end, ?)
来源:https://stackoverflow.com/questions/56551015/how-to-make-method-return-the-same-generic-as-the-input