Multiple if else statements to get non-empty value from a map in Scala - scala

I have a string to string map, and its value can be an empty string. I want to assign a non-empty value to a variable to use it somewhere. Is there a better way to write this in Scala?
import scala.collection.mutable
var keyvalue = mutable.Map.empty[String, String]
keyvalue += ("key1" -> "value1")
var myvalue = ""
if (keyvalue.get("key1").isDefined &&
keyvalue("key1").length > 0) {
myvalue = keyvalue("key1")
}
else if (keyvalue.get("key2").isDefined &&
keyvalue("key2").length > 0) {
myvalue = keyvalue("key2")
}
else if (keyvalue.get("key3").isDefined &&
keyvalue("key3").length > 0) {
myvalue = keyvalue("key3")
}

A more idiomatic way would be to use filter to check the length of the string contained in the Option, then orElse and getOrElse to assign to a val. A crude example:
def getKey(key: String): Option[String] = keyvalue.get(key).filter(_.length > 0)
val myvalue: String = getKey("key1")
.orElse(getKey("key2"))
.orElse(getKey("key3"))
.getOrElse("")
Here's a similar way to do it with an arbitrary list of fallback keys. Using a view and collectFirst, we will only evaluate keyvalue.get for only as many times as we need to (or all, if there are no matches).
val myvalue: String = List("key1", "key2", "key3").view
.map(keyvalue.get)
.collectFirst { case Some(value) if(value.length > 0) => value }
.getOrElse("")

Mmm, it seems it took me too long to devise a generic solution and other answer was accepted, but here it goes:
def getOrTryAgain(map: mutable.Map[String, String], keys: List[String]): Option[String] =
{
if(keys.isEmpty)
None
else
map.get(keys.head).filter(_.length > 0).orElse(getOrTryAgain(map, keys.tail))
}
val myvalue2 = getOrTryAgain(keyvalue, List("key1", "key2", "key3"))
This one you can use to check for as many keys as you want.

Related

passing variable argument data type to a scala method

I want to create a method/function in scala which can take variable arguments of type string or int and return the result of type String or Int.
def Hello(name: String, Param: int*/string*): Int/String= {
var index = 0
while(index < Param.length) {
var n = name
var ar = Param(index)
if ( n.equals(ar) ) return Param(index + 1)
else index = index + 1
}
return Param(index.length -1)
}
If we call the Hello function then it should return the result as given below.
val Res1 = Hello("Jack", "rakesh", 1, "Jack", 2, "No one")
println(Res1)
=> this should return value 2
val Res2 = Hello("ABC", "rakesh", 1, "Jack", 2, "Vik", 3, "ram", 4, "No one")
println(Res2)
=> this should return value "No one"
Using Any should work:
def hello(name: Any, param: Any*): Any= {
var list = param.dropWhile(_ != name)
list.drop(1).headOption.orElse(param.lastOption).getOrElse("")
}
Depending on how type safe you want it to be, you can try to use generics or other means to restrict the types used. Or you can just pattern match the response type:
hello("ABC", "rakesh", 1, "Jack", 2, "Vik", 3, "ram", 4, "No one") match {
case i: Int => println("Got a int:" + i)
case s: String=> println("Got a string:" + s)
}
This will help you
def Hello( name: String,args: Any* ) = {
val index = args.indexOf(name)
if(index == -1)
args(args.length - 1)
else
args(index + 1)
}
Your whole approach is faulty, but here's how it can be done in a type-safe manner.
def Hello(name: String, param: Either[Int,String]*): Either[Int,String] = {
param.sliding(2,2)
.find(_(0).fold(_ => false, _ == name))
.fold(param.last)(_(1))
}
Usage:
Hello("Jack", Right("rakesh"), Left(1), Right("Jack"), Left(2), Right("No one"))
// res0: Either[Int,String] = Left(2)
Hello("ABC", Right("rakesh"), Left(1), Right("Jack"), Left(2),
Right("Vik"), Left(3), Right("ram"), Left(4), Right("No one"))
// res1: Either[Int,String] = Right(No one)
But it would be better to rethink it from the ground up.
I believe, what you want to achieve, is to get an index of a String element(if start counting from 1) in varargs, or return "No one". No need to pass indices to the method. You can do it like this:
def hello(name: String, params: String*): Any = {
val idx = params.indexOf(name)
if (idx != -1) idx + 1 else "No One"
}
Unfortunately both this:
def Hello(name: String, args: Any* ) = {
val index = args.indexOf(name)
if(index == -1)
args(args.length - 1)
else
args(index + 1)
}
and this:
def hello(name: String, param: Any*): Any= {
var index = 0
while(index < param.length) {
var n = name
var ar = param(index)
if ( n.equals(ar) ) return param(index + 1)
else index = index + 1
}
param(index -1)
}
are broken, as they throw an exception if you try to find the index of "No one", as index + 1 will equal to the size of the array. And it's better to compare things in Scala with == for logical equality.
But it's better not to return Any at all, but return Option[Int]:
def hello(name: String, params: String*): Option[Int] =
Option(params.indexOf(name)).filter(_ != -1).map(_ + 1)
So then you can use it like this:
val message1 = hello("Jack", "rakesh" ,"Jack").getOrElse("No one")
val message2 = hello("ABC", "rakesh", "Jack", "Vik", "ram").getOrElse("No one")
Answering the comment:
I want to know how can i pass mixed datatypes to "param".
The simplest way is to have them all of type Any
and also get string or integer as return type
The same way, defining return type as Any
The only small issue here, is that there will be no compile time check against other types. E.g. somebody might pass Boolean or any complex object along with String's and Int's to your function. But you can check at runtime against it or play with types to limit them. I don't know your requirement here, maybe it's even advantage for you.
If having Any is fine, then I would solve it like this:
def Hello(name: Any, params: Any*): Any = Option(params)
.withFilter(_.nonEmpty)
.map(_.indexOf(name))
.filter(i => i != -1 && i < params.length - 1)
.map(i => params(i + 1))
.getOrElse("No one")
Or, if you can assume, params are never empty and you have to use the last param as the default, instead of just hard coded "No one":
def Hello(name: Any, params: Any*): Any = Option(params)
.withFilter(_.nonEmpty)
.map(_.indexOf(name))
.filter(i => i != -1 && i < params.length - 1)
.map(i => params(i + 1))
.getOrElse(params.last)
Notice the check against "No one" attack: i < params.length - 1.
Notice that name now is also of type Any.
Now, even if you pass "No one" as a name, the Option will evaluate to None thanking to the filter, and getOrElse will give you the default "No one" instead of an exception.

Scala Do While Loop Not Ending

I'm new to scala and i'm trying to implement a do while loop but I cannot seem to get it to stop. Im not sure what i'm doing wrong. If someone could help me out that would be great. Its not the best loop I know that but I am new to the language.
Here is my code below:
def mnuQuestionLast(f: (String) => (String, Int)) ={
var dataInput = true
do {
print("Enter 1 to Add Another 0 to Exit > ")
var dataInput1 = f(readLine)
if (dataInput1 == 0){
dataInput == false
} else {
println{"Do the work"}
}
} while(dataInput == true)
}
You're comparing a tuple type (Tuple2[String, Int] in this case) to 0, which works because == is defined on AnyRef, but doesn't make much sense when you think about it. You should be looking at the second element of the tuple:
if (dataInput1._2 == 0)
Or if you want to enhance readability a bit, you can deconstruct the tuple:
val (line, num) = f(readLine)
if (num == 0)
Also, you're comparing dataInput with false (dataInput == false) instead of assigning false:
dataInput = false
Your code did not pass the functional conventions.
The value that the f returns is a tuple and you should check it's second value of your tuple by dataInput1._2==0
so you should change your if to if(dataInput1._2==0)
You can reconstruct your code in a better way:
import util.control.Breaks._
def mnuQuestionLast(f: (String) => (String, Int)) = {
breakable {
while (true) {
print("Enter 1 to Add Another 0 to Exit > ")
f(readLine) match {
case (_, 0) => break()
case (_,1) => println( the work"
case _ => throw new IllegalArgumentException
}
}
}
}

Find String in Char iterator

I have a use case where I need to return a String up to a delimiter String (if found) from an iterator of Char.
The contract:
if iterator is exhausted (only at the begin), return None
if the delimiter String is found, return all characters before it (empty String is fine), delimiter will be dropped
else return the remaining characters
do not eagerly exhaust the iterator!
I do have this working solution, but it feels like Java (which is where I'm coming from)
class MyClass(str: String) {
def nextString(iterator: Iterator[Char]): Option[String] = {
val sb = new StringBuilder
if(!iterator.hasNext) return None
while (iterator.hasNext) {
sb.append(iterator.next())
if (sb.endsWith(str)) return Some(sb.stripSuffix(str))
}
Some(sb.toString())
}
}
Is there a way I can do this in a more functional way (ideally without changing the method signature)?
Update: Here is how I test this
val desmurfer = new MyClass("_smurf_")
val iterator: Iterator[Char] = "Scala_smurf_is_smurf_great_smurf__smurf_".iterator
println(desmurfer.nextString(iterator))
println(desmurfer.nextString(iterator))
println(desmurfer.nextString(iterator))
println(desmurfer.nextString(iterator))
println(desmurfer.nextString(iterator))
println
println(desmurfer.nextString("FooBarBaz".iterator))
println(desmurfer.nextString("".iterator))
Output:
Some(Scala)
Some(is)
Some(great)
Some()
None
Some(FooBarBaz)
None
How about this one:
scala> def nextString(itr: Iterator[Char], sep: String): Option[String] = {
| def next(res: String): String =
| if(res endsWith sep) res dropRight sep.size else if(itr.hasNext) next(res:+itr.next) else res
| if(itr.hasNext) Some(next("")) else None
| }
nextString: (itr: Iterator[Char], sep: String)Option[String]
scala> val iterator: Iterator[Char] = "Scala_smurf_is_smurf_great".iterator
iterator: Iterator[Char] = non-empty iterator
scala> println(nextString(iterator, "_smurf_"))
Some(Scala)
scala> println(nextString(iterator, "_smurf_"))
Some(is)
scala> println(nextString(iterator, "_smurf_"))
Some(great)
scala> println(nextString(iterator, "_smurf_"))
None
scala> println(nextString("FooBarBaz".iterator, "_smurf_"))
Some(FooBarBaz)
What about this one?
def nextString(iterator: Iterator[Char]): Option[String] = {
val t = iterator.toStream
val index = t.indexOfSlice(s)
if(t.isEmpty) None
else if(index == -1) Some(t.mkString)
else Some(t.slice(0,index).mkString)
}
it passed this tests:
val desmurfer = new MyClass("_smurf_")
val iterator: Iterator[Char] = "Scala_smurf_is_smurf_great_smurf__smurf_".iterator
assert(desmurfer.nextString(iterator) == Some("Scala"))
assert(desmurfer.nextString(iterator) == Some("is"))
assert(desmurfer.nextString(iterator) == Some("great"))
assert(desmurfer.nextString(iterator) == Some(""))
assert(desmurfer.nextString(iterator) == None)
assert(desmurfer.nextString("FooBarBaz".iterator) == Some("FooBarBaz"))
assert(desmurfer.nextString("".iterator) == None)
Updated: removed "index == -1 &&" from the first "if condition clause".
This seems to be doing what you'd want. #Eastsun answer motivated me
val str = "hello"
def nextString2(iterator: Iterator[Char]): Option[String] = {
val maxSize = str.size
#tailrec
def inner(collected: List[Char], queue: Queue[Char]): Option[List[Char]] =
if (queue.size == maxSize && queue.sameElements(str))
Some(collected.reverse.dropRight(maxSize))
else
iterator.find(x => true) match {
case Some(el) => inner(el :: collected, if (queue.size == maxSize) queue.dequeue._2.enqueue(el) else queue.enqueue(el))
case None => Some(collected.reverse)
}
if (iterator.hasNext)
inner(Nil, Queue.empty).map(_.mkString)
else
None
}
test(nextString2(Nil.iterator)) === None
test(nextString2("".iterator)) === None
test(nextString2("asd".iterator)) === Some("asd")
test(nextString2("asfhello".iterator)) === Some("asf")
test(nextString2("asehelloasdasd".iterator)) === Some("ase")
But I honestly think it's too complicated to be used. Sometimes you have to use non FP stuff in scala to be performance effecient.
P.S. I didn't know how to match iterator on it's first element, so I've used iterator.find(x => true) which is ugly. Sorry.
P.P.S. A bit of explanation. I recoursively build up collected to fill the elements you are searching for. And I also build queue with last str.size-elements. Then I just check this queue over str each time. This might not be the most efficient way of doing this stuff. You might go with Aho–Corasick algorithm or an analogue if you want more.
P.P.P.S. And I am using iterator as a state, which is probably not FP way
P.P.P.P.S. And you test passes as well:
val desmurfer = new MyClass("_smurf_")
val iterator: Iterator[Char] = "Scala_smurf_is_smurf_great".iterator
test(desmurfer.nextString2(iterator)) === Some("Scala")
test(desmurfer.nextString2(iterator)) === Some("is")
test(desmurfer.nextString2(iterator)) === Some("great")
test(desmurfer.nextString2(iterator)) === None
println()
test(desmurfer.nextString2("FooBarBaz".iterator)) === Some("FooBarBaz")
test(desmurfer.nextString2("".iterator)) === None
Here's one I'm posting just because it's a bit warped :) I wouldn't recommend actually using it:
class MyClass2(str: String) {
val sepLength = str.length
def nextString(iterator: Iterator[Char]): Option[String] = {
if (!iterator.hasNext) return None
val sit = iterator.sliding(sepLength)
val prefix = sit.takeWhile(_.mkString != str).toList
val prefixString = prefix.toList.map(_.head).mkString
if (prefix.head.length < sepLength) Some(prefix.head.mkString)
else if (!iterator.hasNext) Some(prefix.head.mkString + prefix.last.mkString)
else Some(prefixString)
}
}
The idea is that by calling sliding() on our underlying iterator, we can get a sequence, one of which will be our delimiter, if it's present. So we can use takeWhile to find the delimiter. Then the first characters of each of the sliding strings before our delimiter is the string we skipped over. As I said, warped.
I'd really like sliding to be defined so that it produced all subsequences of length n and at the end sequences of length n-1, n-2....1 for this particular use case, but it doesn't, and the horrible if statement at the end is dealing with the various cases.
It passes the test cases :)
Updated: This works without converting the iterator to String
def nextString(iterator: Iterator[Char]): Option[String] = {
if (iterator.isEmpty) None
else Some(iterator.foldLeft("") { (result, currentChar) => if (res.endsWith(str)) result else result + currentChar})
}
A colleague provided the makings of this answer, which is a mixture between his original approach and some polishing from my side. Thanks, Evans!
Then another colleague also added some input. Thanks Ako :-)
class MyClass(str: String) {
def nextString(iterator: Iterator[Char]): Option[String] = {
def nextString(iterator: Iterator[Char], sb: StringBuilder): Option[String] = {
if (!iterator.hasNext || sb.endsWith(str)) {
Some(sb.stripSuffix(str))
} else {
nextString(iterator, sb.append(iterator.next()))
}
}
if (!iterator.hasNext) None
else nextString(iterator, new StringBuilder)
}
}
So far, I like this approach best, so I will accept it in two days unless there is a better answer by then.

Better safe get from an array in scala?

I want to get first argument for main method that is optional, something like this:
val all = args(0) == "all"
However, this would fail with exception if no argument is provided.
Is there any one-liner simple method to set all to false when args[0] is missing; and not doing the common if-no-args-set-false-else... thingy?
In general case you can use lifting:
args.lift(0).map(_ == "all").getOrElse(false)
Or even (thanks to #enzyme):
args.lift(0).contains("all")
You can use headOption and fold (on Option):
val all = args.headOption.fold(false)(_ == "all")
Of course, as #mohit pointed out, map followed by getOrElse will work as well.
If you really need indexed access, you could pimp a get method on any Seq:
implicit class RichIndexedSeq[V, T <% Seq[V]](seq: T) {
def get(i: Int): Option[V] =
if (i < 0 || i >= seq.length) None
else Some(seq(i))
}
However, if this is really about arguments, you'll be probably better off, handling arguments in a fold:
case class MyArgs(n: Int = 1, debug: Boolean = false,
file: Option[String] = None)
val myArgs = args.foldLeft(MyArgs()) {
case (args, "-debug") =>
args.copy(debug = true)
case (args, str) if str.startsWith("-n") =>
args.copy(n = ???) // parse string
case (args, str) if str.startsWith("-f") =>
args.copy(file = Some(???) // parse string
case _ =>
sys.error("Unknown arg")
}
if (myArgs.file.isEmpty)
sys.error("Need file")
You can use foldLeft with initial false value:
val all = (false /: args)(_ | _ == "all")
But be careful, One Liners can be difficult to read.
Something like this will work assuming args(0) returns Some or None:
val all = args(0).map(_ == "all").getOrElse(false)

Is there any way to use immutable collections here + make the code look better?

I have to validate some variables manually for some reason and return a map with the sequance of the error messages for each variable. I've decided to use mutable collections for this because I think there is no other choise left:
val errors = collection.mutable.Map[String, ListBuffer[String]]()
//field1
val fieldToValidate1 = getData1()
if (fieldToValidate1 = "")
errors("fieldToValidate1") += "it must not be empty!"
if (validate2(fieldToValidate1))
errors("fieldToValidate1") += "validation2!"
if (validate3(fieldToValidate1))
errors("fieldToValidate1") += "validation3!"
//field2
val fieldToValidate2 = getData1()
//approximately the same steps
if (fieldToValidate2 = "")
errors("fieldToValidate2") += "it must not be empty!"
//.....
To my mind, it look kind of clumsy and there should other elegant solution. I'd also like to not use mutable collections if possible. Your ideas?
Instead of using mutable collections, you can define errors with var and update it in this way.
var errors = Map[String, List[String]]().withDefaultValue(Nil)
errors = errors updated ("fieldToValidate1", errors("fieldToValidate1") ++ List("it must not be empty!"))
errors = errors updated ("fieldToValidate1", errors("fieldToValidate1") ++ List("validation2"))
The code looks more tedious, but it gets out of mutable collections.
So what is a good type for your check? I was thinking about A => Option[String] if A is the type of your object under test. If your error messages do not depend on the value of the object under test, (A => Boolean, String) might be more convenient.
//for constructing checks from boolean test and an error message
def checkMsg[A](check: A => Boolean, msg: => String): A => Option[String] =
x => if(check(x)) Some(msg) else None
val checks = Seq[String => Option[String]](
checkMsg((_ == ""), "it must not be empty"),
//example of using the object under test in the error message
x => Some(x).filterNot(_ startsWith "ab").map(x => x + " does not begin with ab")
)
val objectUnderTest = "acvw"
val errors = checks.flatMap(c => c(objectUnderTest))
Error labels
As I just noted, you were asking for a map with a label for each check. In this case, you need to provide the check label, of course. Then the type of your check would be (String, A => Option[String]).
Although a [relatively] widespread way of doing-the-thing-right would be using scalaz's Validation (as #senia has shown), I think it is a little bit overwhelming approach (if you're bringing scalaz to your project you have to be a seasoned scala developer, otherwise it may bring you more harm than good).
Nice alternative could be using ScalaUtils which has Or and Every specifically made for this purpose, in fact if you're using ScalaTest you already have seen an example of them in use (it uses scalautils underneath). I shamefully copy-pasted example from their doc:
import org.scalautils._
def parseName(input: String): String Or One[ErrorMessage] = {
val trimmed = input.trim
if (!trimmed.isEmpty) Good(trimmed) else Bad(One(s""""${input}" is not a valid name"""))
}
def parseAge(input: String): Int Or One[ErrorMessage] = {
try {
val age = input.trim.toInt
if (age >= 0) Good(age) else Bad(One(s""""${age}" is not a valid age"""))
}
catch {
case _: NumberFormatException => Bad(One(s""""${input}" is not a valid integer"""))
}
}
import Accumulation._
def parsePerson(inputName: String, inputAge: String): Person Or Every[ErrorMessage] = {
val name = parseName(inputName)
val age = parseAge(inputAge)
withGood(name, age) { Person(_, _) }
}
parsePerson("Bridget Jones", "29")
// Result: Good(Person(Bridget Jones,29))
parsePerson("Bridget Jones", "")
// Result: Bad(One("" is not a valid integer))
parsePerson("Bridget Jones", "-29")
// Result: Bad(One("-29" is not a valid age))
parsePerson("", "")
// Result: Bad(Many("" is not a valid name, "" is not a valid integer))
Having said this, I don't think you can do any better than your current approach if you want to stick with core scala without any external dependencies.
In case you can use scalaz the best solution for aggregation errors is Validation:
def validate1(value: String) =
if (value == "") "it must not be empty!".failNel else value.success
def validate2(value: String) =
if (value.length > 10) "it must not be longer than 10!".failNel else value.success
def validate3(value: String) =
if (value == "error") "it must not be equal to 'error'!".failNel else value.success
def validateField(name: String, value: String): ValidationNel[(String, String), String] =
(
validate1(value) |#|
validate2(value) |#|
validate3(value)
).tupled >| value leftMap { _.map{ name -> _ } }
val result = (
validateField("fieldToValidate1", getData1()) |#|
validateField("fieldToValidate2", getData2())
).tupled
Then you could get optional errors Map like this:
val errors =
result.swap.toOption.map{
_.toList.groupBy(_._1).map{ case (k, v) => k -> v.map(_._2) }
}
// Some(Map(fieldToValidate2 -> List(it must not be equal to 'error'!), fieldToValidate1 -> List(it must not be empty!)))