I am trying to get the first 2 values of a comma separated string in scala. For example
a,b,this is a test
How do i store the values a,b in 2 separate variables?
To keep it easy and clean.
KISS solution:
1.Use split for separation. Then use take which is defined on all ordered sequences to get the elements as needed:
scala> val res = "a,b,this is a test" split ',' take 2
res: Array[String] = Array(a, b)
2.Use Pattern matching to set the variables:
scala> val Array(x,y) = res
x: String = a
y: String = b*
Another solution using Sequence Pattern match in Scalaenter link description here
Welcome to Scala version 2.11.2 (OpenJDK 64-Bit Server VM, Java 1.7.0_65).
Type in expressions to have them evaluated.
Type :help for more information.
scala> val str = "a,b,this is a test"
str: String = a,b,this is a test
scala> val Array(x, y, _*) = str.split(",")
x: String = a
y: String = b
scala> println(s"x = $x, y = $y")
x = a, y = b
Are you looking for the method split ?
"a,b,this is a test".split(',')
res0: Array[String] = Array(a, b, this is a test)
If you want only the first two values you'll need to do something like:
val splitted = "a,b,this is a test".split(',')
val (first, second) = (splitted(0), splitted(1))
There should be some regex options here.
scala> val s = "a,b,this is a test"
s: String = a,b,this is a test
scala> val r = "[^,]+".r
r: scala.util.matching.Regex = [^,]+
scala> r findAllIn s
res0: scala.util.matching.Regex.MatchIterator = non-empty iterator
scala> .toList
res1: List[String] = List(a, b, this is a test)
scala> .take(2)
res2: List[String] = List(a, b)
scala> val a :: b :: _ = res2
a: String = a
b: String = b
but
scala> val a :: b :: _ = (r findAllIn "a" take 2).toList
scala.MatchError: List(a) (of class scala.collection.immutable.$colon$colon)
... 33 elided
or if you're not sure there is a second item, for instance:
scala> val r2 = "([^,]+)(?:,([^,]*))?".r.unanchored
r2: scala.util.matching.UnanchoredRegex = ([^,]+)(?:,([^,]*))?
scala> val (a,b) = "a" match { case r2(x,y) => (x, Option(y)) }
a: String = a
b: Option[String] = None
scala> val (a,b) = s match { case r2(x,y) => (x, Option(y)) }
a: String = a
b: Option[String] = Some(b)
This is a bit nicer if records are long strings.
Footnote: the Option cases look nicer with a regex interpolator.
If your string is short, you may as well just use String.split and take the first two elements.
val myString = "a,b,this is a test"
val splitString = myString.split(',') // Scala adds a split-by-character method in addition to Java's split-by-regex
val a = splitString(0)
val b = splitString(1)
Another solution would be to use a regex to extract the first two elements. I think it's quite elegant.
val myString = "a,b,this is a test"
val regex = """(.*),(.*),.*""".r // all groups (in parenthesis) will be extracted.
val regex(a, b) = myString // a="a", b="b"
Of course, you can tweak the regex to only allow non-empty tokens (or anything else you might need to validate) :
val regex = """(.+),(.+),.+""".r
Note that in my examples I assumed that the string always had at least two tokens. In the first example, you can test the length of the array if needed. The second one will throw a MatchError if the regex doesn't match the string.
I had originally proposed the following solution. I will leave it because it works and doesn't use any class formally marked as deprecated, but the Javadoc for StringTokenizer mentions that it is a legacy class and should no longer be used.
val myString = "a,b,this is a test"
val st = new StringTokenizer(",");
val a = st.nextToken()
val b = st.nextToken()
// You could keep calling st.nextToken(), as long as st.hasMoreTokens is true
Related
This is working fine,
val Array(a,b) = "Hello,Bye".split(',')
But it is an error because extra-information is not ignored:
val Array(a,b) = "Hello,Bye,etc".split(',')
// scala.MatchError: ...
how to ignore extra-information?
Same error in the case of less items:
val Array(a,b) = "Hello".split(',')
IMPORTANT: no elegant way like the Javascript Destructuring assignment?
Add a placeholder using underscore:
val Array(a,b, _) = "Hello,Bye,etc".split(',')
EDIT: Using match-case syntax is generally more preferred and more flexible (and you can catch all possible outcome):
val s = "Hello,Bye,etc"
s.split(',') match {
case Array(a) => //...
case Array(a, b) => //...
case Array(a, b, rest#_*) => //...
case _ => //Catch all case to avoid MatchError
}
#_ will cover both instances.
val Array(a,b,x#_*) = "Hello,Bye,etc".split(',')
//a: String = Hello
//b: String = Bye
//x: Seq[String] = ArraySeq(etc)
val Array(c,d,z#_*) = "Hello,Bye".split(',')
//c: String = Hello
//d: String = Bye
//z: Seq[String] = ArraySeq()
From your comments it looks like you want to default to "", an empty String. I found a way to do it with Stream, which has been deprecated in Scala 2.13, but so far it is the cleanest solution I've found.
val Stream(a,b,c,d,_*) = "one,two,etc".split(",").toStream ++ Stream.continually("")
//a: String = one
//b: String = two
//c: String = etc
//d: String = ""
I would consider making the result values of type Option[String] by lift-ing the split Array[String] (viewed as a partial function) into an Int => Option[String] function:
val opts = "Hello".split(",").lift
// opts: Int => Option[String] = <function1>
opts(0)
// res1: Option[String] = Some(Hello)
opts(1)
// res2: Option[String] = None
Or, if String values are preferred with None translated to "":
val strs = "Hello,world".split(",").lift.andThen(_.getOrElse(""))
// strs: Int => String = scala.Function1$$Lambda$...
strs(0)
// res3: String = Hello
strs(1)
// res4: String = "world"
strs(2)
// res5: String = ""
Note that with this approach, you can take as many opts(i) or strs(i), i = 0, 1, 2, ..., as wanted.
You can do this by converting to List first:
val a :: b :: _ = "Hello,Bye,etc".split(',').toList
Is there a supported way to achieve a conversion of any numeric type to a double. E.g.
val i = 12345
val f = 1234.5F
val d = 1234.5D
val arr = Array[Any](i,f,d)
val anotherD = arr(0).asInstanceOf[Numeric].toDouble
Naturally the above code is not correct as given - since Numeric requires Type arguments.
scala> val i = 12345
i: Int = 12345
scala> val f = 1234.5F
f: Float = 1234.5
scala> val d = 1234.5D
d: Double = 1234.5
scala> val arr = Array[Any](i,f,d)
arr: Array[Any] = Array(12345, 1234.5, 1234.5)
scala> val anotherD = arr(0).asInstanceOf[Numeric].toDouble
<console>:11: error: type Numeric takes type parameters
val anotherD = arr(0).asInstanceOf[Numeric].toDouble
Now I realize the above may be achieved via match/case , along the following lines:
(a, e) match {
case (a : Double, e : Double) =>
Math.abs(a - e) <= CompareTol
case (a : Float, e : Float) =>
Math.abs(a - e) <= CompareTol
.. etc
But I was wondering if there were a means to more compactly express the operation. This code is within TEST classes and efficiency is not an important criterion. Specifically: reflection calls are OK. Thanks.
I assume you are on the JVM. The Number class does like what you want to achieve with the doubleValue method:
val arr = Array[Number](i,f,d)
val ds = arr.map(_.doubleValue())
This is horrible, and probably not efficient, but it works (on your example) :p
scala> import scala.language.reflectiveCalls
import scala.language.reflectiveCalls
scala> arr.map(_.asInstanceOf[{ def toDouble: Double }].toDouble)
res2: Array[Double] = Array(12345.0, 1234.5, 1234.5)
Besides using match, is there an Option-like way to getOrElse the actual content of the Right or Left value?
scala> val x: Either[String,Int] = Right(5)
scala> val a: String = x match {
case Right(x) => x.toString
case Left(x) => "left"
}
a: String = 5
Nicolas Rinaudo's answer regarding calling getOrElse on either the left or right projection is probably the closest to Option.getOrElse.
Alternatively, you can fold the either:
scala> val x: Either[String,Int] = Right(5)
x: Either[String,Int] = Right(5)
scala> val a: String = x.fold(l => "left", r => r.toString)
a: String = 5
As l is not used in the above fold, you could also write x.fold(_ => "left", r => r.toString)
Edit:
Actually, you can literally have Option.getOrElse by calling toOption on the left or right projection of the either, eg,
scala> val o: Option[Int] = x.right.toOption
o: Option[Int] = Some(5)
scala> val a: String = o.map(_.toString).getOrElse("left")
a: String = 5
I don't particularly like Either and as a result I'm not terribly familiar with it, but I believe you're looking for projections: either.left.getOrElse or either.right.getOrElse.
Note that projections can be used in for-comprehensions as well. This is an example straight from the documentation:
def interactWithDB(x: Query): Either[Exception, Result] =
try {
Right(getResultFromDatabase(x))
} catch {
case ex => Left(ex)
}
// this will only be executed if interactWithDB returns a Right
val report =
for (r <- interactWithDB(someQuery).right) yield generateReport(r)
if (report.isRight)
send(report)
else
log("report not generated, reason was " + report.left.get)
Given type A on both sides, that is, Either[A, A], we can use Either.merge
...to extract values from Either instances regardless of whether they are
Left or Right.
Note if left and right types differ then result is least upper bound of the two types which may become in worst case Any:
val e: Either[Int, String] = Right("hello")
e.merge // hello: Any
In Scala 2.12 there is a getOrElse method for getting the "right" value but you cannot use it for the "left" value directly. However, you can do it like this: e.swap.getOrElse(42).
Given the following:
val x = Some(Some(1))
What would be the cleanest way to get the 1 (or -1 if the one did not exist)?
I'm creating an object instance from a tuple returned from a database query. One of the values in the tuple looks like this, so I would like a nice short 'one liner' to just get the value or set a parameter to -1.
x.flatten is what you are looking for. Here it will give you Some(1).
If you really want to get -1 for the case where "the one did not exist", just do x.flatten.getOrElse(-1):
scala> Some(Some(1)).flatten.getOrElse(-1)
res1: Int = 1
scala> Some(None).flatten.getOrElse(-1)
res2: Int = -1
scala> None.flatten.getOrElse(-1)
res3: Int = -1
for-comprehensions are often a very readable way to use these kinds of nested structures:
val x = Some(Some(1))
val result = for {
firstLevel <- x
secondLevel <- firstLevel
} yield {
// We've got an int, now transform it!
(secondLevel * 100).toString
}
The result is an Option[String], and the transformation only happens when you have two Some(s).
You can also use pattern matching:
val result2 = for {
Some(v) <- x
} yield {
// We've got a int, now transform it!
(v * 100).toString
}
Although not a "one-liner" you could make a function to extract the value. If you make it generic and pass in the default value it might be pretty handy.
scala> def fetchFromOptions[A](default: A)(x: Option[Option[A]]) = x match {
| case Some(Some(a)) => a
| case _ => default
| }
fetchFromOptions: [A](default: A)(x: Option[Option[A]])A
scala> fetchFromOptions(-1)(Some(Some(1)))
res0: Int = 1
scala> fetchFromOptions(-1)(Some(None))
res1: Int = -1
This is what I've come up with:
scala> val x = Some(Some(1))
x: Some[Some[Int]] = Some(Some(1))
scala> val y = x.map(_.getOrElse(-1)).get
y: Int = 1
scala> val x = Some(None)
x: Some[None.type] = Some(None)
scala> val y = x.map(_.getOrElse(-1)).get
y: Int = -1
This only works when your first-level Some is not None
If you actually know that its Some(Some(1)), then you can use the irrefutable pattern match notation:
scala> val ssi1 = Some(Some(1))
ssi1: Some[Some[Int]] = Some(Some(1))
scala> val Some(Some(i1)) = ssi1
i1: Int = 1
If there may be any of the possible None in the mix, then you have to use the more cautious and verbose forms suggested by others.
For those that find this a bizarre notation, think of it as what you'd write in a case in a match construct or PartialFunction literal to match against a scrutinee that is Some(Some(1)).
Edit
originally the question was "Collection to Tuple" as I assumed I needed a tuple in order to do variable multi-assignment. It turns out that one can do variable multi-assignment directly on collections. Retitled the question accordingly.
Original
Have a simple Seq[String] derived from a regex that I would like to convert to a Tuple.
What's the most direct way to do so?
I currently have:
val(clazz, date) = captures match {
case x: Seq[String] => (x(0), x(1))
}
Which is ok, but my routing layer has a bunch of regex matched routes that I'll be doing val(a,b,c) multi-assignment on (the capture group is always known since the route is not processed if regex does not match). Would be nice to have a leaner solution than match { case.. => ..}
What's the shortest 1-liner to convert collections to tuples in Scala?
This is not an answer to the question but might solve the problem in a different way.
You know you can match a xs: List[String] like so:
val a :: b :: c :: _ = xs
This assigns the first three elements of the list to a,b,c? You can match other things like Seq in the declaration of a val just like inside a case statement. Be sure you take care of matching errors:
Catching MatchError at val initialisation with pattern matching in Scala?
You can make it slightly nicer using |> operator from Scalaz.
scala> val captures = Vector("Hello", "World")
captures: scala.collection.immutable.Vector[java.lang.String] = Vector(Hello, World)
scala> val (a, b) = captures |> { x => (x(0), x(1)) }
a: java.lang.String = Hello
b: java.lang.String = World
If you don't want to use Scalaz, you can define |> yourself as shown below:
scala> class AW[A](a: A) {
| def |>[B](f: A => B): B = f(a)
| }
defined class AW
scala> implicit def aW[A](a: A): AW[A] = new AW(a)
aW: [A](a: A)AW[A]
EDIT:
Or, something like #ziggystar's suggestion:
scala> val Vector(a, b) = captures
a: java.lang.String = Hello
b: java.lang.String = World
You can make it more concise as shown below:
scala> val S = Seq
S: scala.collection.Seq.type = scala.collection.Seq$#157e63a
scala> val S(a, b) = captures
a: java.lang.String = Hello
b: java.lang.String = World
As proposed by #ziggystar in comments you can do something like:
val (clazz, date) = { val a::b::_ = capture; (a, b)}
or
val (clazz, date) = (capture(0), capture(1))
If you verified the type of the list before it is OK, but take care of the length of the Seq because the code will run even if the list is of size 0 or 1.
Your question is originally specifically about assigning the individual capturing groups in a regex, which already allow you to assign from them directly:
scala> val regex = """(\d*)-(\d*)-(\d*)""".r
regex: scala.util.matching.Regex = (\d*)-(\d*)-(\d*)
scala> val regex(a, b, c) = "29-1-2012"
d: String = 29
m: String = 1
y: String = 2012
obviously you can use these in a case as well:
scala> "29-1-2012" match { case regex(d, m, y) => (y, m, d) }
res16: (String, String, String) = (2012,1,29)
and then group these as required.
Seqs to tuple
To perform multi-assignment from a Seq, what about the following?
val Seq(clazz, date) = captures
As you see, no need to restrict to Lists; this code will throw a MatchError if the length does not match (in your case, that's good because it means that you made a mistake). You can then add
(clazz, date)
to recreate the tuple.
Tuples from matches
However, Jed Wesley-Smith posted a solution which avoids this problem and solves the original question better. In particular, in your solution you have a Seq whose length is not specified, so if you make a mistake the compiler won't tell you; with tuples instead the compiler can help you (even if it can't check against the regexp).