Cleanest way to check if a string value is a negative number? - scala

We could make a method:
def isNegNumber(s : String) : Boolean =
(s.head == '-') && s.substring(1).forall(_.isDigit)
Is there a cleaner way to do this?

You can use Try and Option to do this in a safer way. This will prevent an error if the string is not a number at all
import scala.util.Try
val s = "-10"
val t = Try(s.toDouble).toOption
val result = t.fold(false)(_ < 0)
Or even better, based on Luis' comment, starting Scala 2.13 (simpler and more efficient):
val t = s.toDoubleOption
val result = t.fold(false)(_ < 0)

Use a regex pattern.
"-10" matches "-\\d+" //true
It can easily be adjusted to account for whitespace.
"\t- 7\n" matches raw"\s*-\s*\d+\s*" //true

Another possible solution could be:
def isNegativeNumber(s: String) = s.toDoubleOption.exists(_ < 0)

Related

Scala filter by extension

I have this function below for filter a list of files. I was wondering how I could filter so it only returns files that end in .png or .txt?
def getListOfFiles(directoryName: String): Array[String] = {
return (new File(directoryName)).listFiles.filter(_.isFile).map(_.getAbsolutePath)
}
Thanks for the help, guys.
Just add a condition to filter:
(new File(directoryName)).listFiles.
filter { f => f.isFile && (f.getName.endsWith(".png") || f.getName.endsWith(".txt")) }.
map(_.getAbsolutePath)
or use listFiles(FileFilter) instead of just listFiles, but it's less convenient (unless you use experimental Scala single method interface implementation)
Just like you would filter ordinary strings:
val filenames = List("batman.png", "shakespeare.txt", "superman.mov")
filenames.filter(name => name.endsWith(".png") || name.endsWith(".txt"))
// res1: List[String] = List(batman.png, shakespeare.txt)
Alternative approach, a bit less verbose
import scala.reflect.io.Directory
Directory(directoryName).walkFilter(_.extension=="png")
It returns an Iterator[Path] which can be converted with.toArray[String]

Best way to write a generic method that checks whether a number is 0 in scala

I know this is very trivial to do with a simple case check, but it seems it should be possible to write a method that does something like the following, that is generalizes to all numeric types:
def checkNonZero(t: Long, field: String): List[String] = {
if (t == 0) println("was zero") else println("wasn't zero")
}
What's the best way to do this?
This can be done with the Number type.
def checkNonZero(n: Number) = n != 0
Or you can use the Numeric typeclass.
def checkNonZero[T : Numeric](n: T) = {
val num = implicitly[Numeric[T]]
!num.equiv(n, num.zero)
}
EDIT
Actually, you can just write it like this:
def checkNonZero[T : Numeric](n: T) = n != 0
Unless you define new custom instances of Numeric of course.
An alternative way to Jasper-M's second solution goes as follows:
def isNotZero[A](n: A)(implicit num: Numeric[A]) = !num.equiv(n, num.zero)
This saves the line val num = implicitly[Numeric[A]], while still allowing you to work with num if you so desire.
Despite this method actually taking 2 parameters, because the second one's implicit, you can use it like you would any other non-curried single-parameter method:
isNotZero(3) // true
isNotZero(0.1) // true
isNotZero(0) // false
isNotZero(0.0) // false

What is a more Scala way of writing this code?

I want to strip off the word "America/" from the start of each item in the list, and the code below does just that, but I feel like it can be done in a significantly better way.
var tz = java.util.TimeZone.getAvailableIDs
for(i <- 0 until tz.length) {
if(tz(i).startsWith("America/")) {
tz(i) = tz(i).replaceFirst("America/", "")
}
}
Simple and straight forward:
val tz = java.util.TimeZone.getAvailableIDs.map(_.replaceFirst("^America/", ""))
very similar to #Noah's answer, but using a for-yield iteration (so that you can add other filters with no more usage of parentheses).
import java.util.TimeZone
val tz = for(t <- TimeZone.getAvailableIDs) yield t.replaceFirst("^America/", "")
I will use regex for it:
val pattern = "^America/".r
tz = tz.map(pattern.replaceFirstIn(_, ""))
wonder if it is an effcient way.
Map is preferred to for loops in functional programming, so instead of changing the list in place with a for loop, passing the data around by mapping is more pure and (IMO) prettier.
this can work:
val tzs = java.util.TimeZone.getAvailableIDs map { tz =>
if(tz.startsWith("America/")) tz.replaceFirst("America/","")
else tz
}
If you only want the American time zones, you could do this:
val americanZones = {
val pattern = "^America/(.*)".r
( java.util.TimeZone.getAvailableIDs
flatMap pattern.findFirstMatchIn
map (_ group 1) )
}
Added an 'if' to the for/yield
val zones = java.util.TimeZone.getAvailableIDs
val formatted_zones = for(i <- 0 until zones.length if zones(i).startsWith("America/")) yield {
zones(i).replaceFirst("America/", "")
}
No regex:
val tz = java.util.TimeZone.getAvailableIDs.map(_ stripPrefix "America/")

Can anyone 1-line this Scala code (preferably with FP?)

Sup y'all. The below feels like a tragic waste of Scala. Can anyone save this code?
val tokenSplit = token.split(":")(1)
val candidateEntityName = tokenSplit.substring(0,tokenSplit.length-1)
if(!candidateEntityName.equals(entityName)) removeEnd = true
Here it is (You don't need to use equals):
val removeEnd = token.split(":")(1).init != entityName
should be sth like: (untested)
val removeEnd = !(token.split(":")(1).dropRight(1).equals(entityName))
or:
val removeEnd = !(token.split(":").last.dropRight(1).equals(entityName))
A different solution using regex's to match on the input. It also handles the case where the data isn't as expected (you could of course extend your regex to suite your needs).
val removeEnd = """<(\w+):(\w+)>""".r.findFirstMatchIn(token).map(!_.group(2).equals(entityName)).getOrElse(throw new Exception(s"Can't parse input: $token"))
If you want to default to false:
val removeEnd = """<(\w+):(\w+)>""".r.findFirstMatchIn(token).exists(!_.group(2).equals(entityName))

Does Scala have a library method to build Option-s that takes into account empty strings?

I want to filter out empty strings to put them into an Option. So I quickly built this now:
def StrictOption(s: String) = s match {
case s if s != null && s.trim.length() > 0 => Some(s)
case _ => None
}
Question: is this maybe already somewhere in the standard library?
I don't think there's one single method in the standard library to do this, but you can do this much more tersely than your implementation.
Option(s).filter(_.trim.nonEmpty)
If you care at all about performance then
if (s.trim.isEmpty) None else Some(s)
is only 4 characters longer than Ben James's solution, and runs 3 times faster, in my benchmark (47 vs 141).
Starting Scala 2.13, for those not expecting nulls (non-Java context), Option::unless and Option::when are now an alternative option:
// val str = "hello"
Option.unless(str.isEmpty)(str)
// Option[String] = Some(hello)
Option.when(str.nonEmpty)(str)
// Option[String] = Some(hello)
// val str: String = ""
Option.unless(str.isEmpty)(str)
// Option[String] = None
Option.when(str.nonEmpty)(str)
// Option[String] = None
There's nothing built in; Ben's filter is the best brief version if performance isn't an issue (e.g. you're validating user input). Typically, performance will not be an issue.
Also, note that it's a little strange to use match when you're not actually matching anything; it's just more boilerplate to get an if-else statement. Just say
if (s ne null && s.trim.length > 0) Some(s) else None
which is about as fast and brief as anything, unless you want to write your own is-it-whitespace method. Note that trim uses a peculiar criterion: anything above \u0020 (i.e. ' ') is not trimmed, and anything equal or below is. So you could also write your own trimmed-string-is-empty detector, if performance of this operation was particularly important:
def ContentOption(s: String): Option[String] = {
if (s ne null) {
var i = s.length-1
while (i >= 0) {
if (s.charAt(i) > ' ') return Some(s)
i -= 1
}
}
None
}
This could also be achieved with a for-comprehension
val res = for (v <- Option(s) if s.nonEmpty) yield v
Option("something") produces Some("something")
Option(null) produces None