I am using scalatest FlatSpec Library in order to test my code.
Below is the function I wrote in order to test my code:
def compareToHello (a:String) = {
a match {case "Hello" => println ("Hello")
case _ => println("error")}
}
For the test part :
import org.scalatest.{ FlatSpec, GivenWhenThen, Matchers }
class AestA extends FlatSpec with GivenWhenThen with Matchers {
"Implemented function" should "compare input string to hello"in {
val test=compareToHello("Hello)
assert
}
}
I am facing a problem regarding what to put in the assert as the output of my function is displayed in console.
I am learning scala , that is why I am asking this kind of questions
Thanks a lot
println is a side effect: you won't be able to reason about the output of compareToHello.
Recall
scala> def f: Unit = println("Hello")
f: Unit
scala> f
Hello
scala> f == "Hello"
<console>:9: warning: comparing values of types Unit and String using `==' will always yield false
f == "Hello"
^
Hello
res7: Boolean = false
scala> f == ()
<console>:9: warning: comparing values of types Unit and Unit using `==' will always yield true
f == ()
^
warning: there was one deprecation warning; re-run with -deprecation for details
Hello
res8: Boolean = true
What you can do is to make this function pure, by returning a string instead:
def compareToHello (a:String) = a match {
case "Hello" => "Hello"
case _ => "error"
}
and assert as follows:
assert(test == "Hello")
Related
I'm reading a text file and have the syntax set up correctly to do so. What I want to do now is append all the integers into an array, but when I try to use a print statement to check what's going on, nothing shows up in the terminal.
package lecture
import scala.io.{BufferedSource, Source}
object LectureQuestion {
def fileSum(fileName: String): Int = {
var arrayOfnumbers = Array[String]()
var fileOfnumbers: BufferedSource = Source.fromFile(fileName)
for (line <- fileOfnumbers.getLines()){
val splits: Array[String] =line.split("#")
for (number <- splits){
arrayOfnumbers :+ number
println(arrayOfnumbers.mkString(""))
}
//println(splits.mkString(" "))
}
3
}
def main(args: Array[String]): Unit = {
println(fileSum("data/fileOfnumbers.txt"))
}
}
I set up a blank array to append the numbers to. I tried switching var to val, but that wouldn't make sense as var is mutuable, meaning it can change. I'm pretty sure the way to add things to an array in Scala is :+, so I'm not sure what's going on.
In Scala all you would need is flatMap the List of a List and then sum the result.
Here your example simplified, as we have extracted the lines already:
import scala.util.Try
def listSum(lines: List[String]): Int = {
(for{
line <- lines
number <- line.split("#").map(n => Try(n.trim.toInt).getOrElse(0))
} yield number).sum
}
listSum(List("12#43#134#bad","13#54#47")) // -> 303
No vars, resp. no mutability needed. Just a nice for-comprehension;).
And for comparison the solution with flatMap:
def listSum(lines: List[String]): Int = {
lines
.flatMap(_.split("#").map(n => Try(n.trim.toInt).getOrElse(0)))
.sum
}
I have a helper class which creates instance of another class
class TestEnv {
val questionsController = new QuestionsController(...)
}
I am unit testing QuestionsController and have created a basic test case
class QuestionsControllerUnitSpec extends PlaySpec with BeforeAndAfterAll with BeforeAndAfterEach with OneAppPerSuiteWithComponents{
override def beforeEach() = {
println("------------new test -----------------")
}
override def components: BuiltInComponents = new BuiltInComponentsFromContext(context) with NoHttpFiltersComponents {
import play.api.mvc.Results
import play.api.routing.Router
import play.api.routing.sird._
lazy val router: Router = Router.from({
case GET(p"/") => defaultActionBuilder {
Results.Ok("success!")
}
})
}
"Question Controller " should {
"be created" in {
val testEnv = new TestEnv(components = components)
val qc:QuestionsController = testEnv.questionsController
qc mustBe defined //I get compilation error
}
}
}
I get the following compilation error
Error:(52, 10) could not find implicit value for parameter definition: org.scalatest.enablers.Definition[controllers.QuestionsController]
qc mustBe defined
Error:(52, 10) not enough arguments for method mustBe: (implicit definition: org.scalatest.enablers.Definition[controllers.QuestionsController])org.scalatest.Assertion.
Unspecified value parameter definition.
qc mustBe defined
I checked the definition of mustBe in MustMatchers.class. It is defined as def mustBe(right : org.scalatest.words.DefinedWord)(implicit definition : org.scalatest.enablers.Definition[T]) : org.scalatest.Assertion = { /* compiled code */ }
Why am I getting the error.
defined matcher syntax can be used with user defined types if we provide implicit implementation of Definition trait. For example, say we have a user defined class
class Foo {
val bar = 3
}
and we provide implicit definition
implicit val fooDefinition = new Definition[Foo] {
override def isDefined(foo: Foo): Boolean = foo.bar != null
}
then we can use defined syntax
(new Foo()) mustBe defined
If similar implicit implementation of Definition[QuestionsController] is provided, then the compiler error should be resolved.
I am happy to accept a different answer if it can provide more accurate answer. I suppose I am testing the wrong thing. What I am doing is similar to declaring an integer and checking if the integer exists! Instead I should be checking the value of the integer.
About matchers, more information is at http://doc.scalatest.org/3.0.1/#org.scalatest.MustMatchers. More information on Definition is on http://doc.scalatest.org/3.0.1/#org.scalatest.enablers.Definition
I'm new to Kotlin (I have a Java background) and I can't seem to figure out how to check whether a string contains a match from a list of keywords.
What I want to do is check if a string contains a match from an array of keywords (case-insensitive please). If so, print out the keyword(s) that was matched and the string that contained the keyword. (I will be looping over a bunch of strings in a file).
Here's an MVE for starters:
val keywords = arrayOf("foo", "bar", "spam")
fun search(content: String) {
var match = <return an array of the keywords that content contained>
if(match.size > 0) {
println("Found match(es): " + match + "\n" + content)
}
}
fun main(args: Array<String>) {
var str = "I found food in the barn"
search(str) //should print out that foo and bar were a match
}
As a start (this ignores the 'match' variable and getting-a-list-of-keywords-matched), I tried using the following if statement according with what I found at this question,
if(Arrays.stream(keywords).parallel().anyMatch(content::contains))
but it put a squiggly line under "content" and gave me this error
None of the following functions can be called with the arguments
supplied: public operator fun CharSequence.contains(char: Char,
ignoreCase: Boolean = ...): Boolean defined in kotlin.text public
operator fun CharSequence.contains(other: CharSequence, ignoreCase:
Boolean = ...): Boolean defined in kotlin.text #InlineOnly public
inline operator fun CharSequence.contains(regex: Regex): Boolean
defined in kotlin.text
You can use the filter function to leave only those keywords contained in content:
val match = keywords.filter { it in content }
Here match is a List<String>. If you want to get an array in the result, you can add .toTypedArray() call.
in operator in the expression it in content is the same as content.contains(it).
If you want to have case insensitive match, you need to specify ignoreCase parameter when calling contains:
val match = keywords.filter { content.contains(it, ignoreCase = true) }
Another obvious choice is using a regex doing case-insensitive matching:
arrayOf("foo", "bar", "spam").joinToString(prefix = "(?i)", separator = "|").toRegex())
Glues together a pattern with a prefixed inline (?i) incase-sensitive modifier, and alternations between the keywords: (?i)foo|bar|spam
Sample Code:
private val keywords = arrayOf("foo", "bar", "spam")
private val pattern = keywords.joinToString(prefix = "(?i)", separator = "|")
private val rx = pattern.toRegex()
fun findKeyword(content: String): ArrayList<String> {
var result = ArrayList<String>()
rx.findAll(content).forEach { result.add(it.value) }
return result
}
fun main(args: Array<String>) {
println(findKeyword("Some spam and a lot of bar"));
}
The regex approach could be handy if you are after some more complex matching, e.g. non-/overlapping matches adding word boundaries \b, etc.
Here is my approach without Streams:
fun String.containsAnyOfIgnoreCase(keywords: List<String>): Boolean {
for (keyword in keywords) {
if (this.contains(keyword, true)) return true
}
return false
}
Usage:
"test string".containsAnyOfIgnoreCase(listOf("abc","test"))
I think Any is the efficient way.
fun findMatch(s: String, strings: List<String>): Boolean {
return strings.any { s.contains(it) }
}
fun main() {
val today = "Wednesday"
val weekend = listOf("Sat", "Sun")
println(if (findMatch(today, weekend)) "Yes" else "No") // No
}
reference: click here
From array.scala of scala-2.10.4, The Array is defined as
final class Array[T](_length: Int) extends java.io.Serializable with java.lang.Cloneable {
/** The length of the array */
def length: Int = throw new Error()
def apply(i: Int): T = throw new Error()
def update(i: Int, x: T) { throw new Error() }
override def clone(): Array[T] = throw new Error()
}
Please note, the apply method will throw an exception! And for the accompany object Arrry, I find the following codes:
def apply[T: ClassTag](xs: T*): Array[T] = {
val array = new Array[T](xs.length)
var i = 0
for (x <- xs.iterator) { array(i) = x; i += 1 }
array
}
I know there is an implicit parameter which is ClassTag[T], what make me surprised is how
new Array[T] (xs.length)
is compiled. By decompiling the Array.class, I find that line is translated to :
public <T> Object apply(Seq<T> xs, ClassTag<T> evidence$2)
{
// evidence$2 is implicit parameter
Object array = evidence$2.newArray(xs.length());
...
}
I am really confused by this kind of translation, what is the rule under the hood?
Thanks
Chang
The Scala Array Class is just a fake wrapper for the runtime so you can use arrays in Scala. You're probably confused because those methods on the Array class throw exceptions. The reason they did this is so that if you actually end up using the fake class it blows up since really it should be using the java runtime array, which does not have a proper container class like Scala. You can see how the compiler handles it here. When your using arrays in Scala you're probably also using some implicits from predef like ArrayOps and WrappedArray for extra helper methods.
TLDR: Scala compiler magic makes arrays work with the java runtime under the hood.
On the JVM arrays are exempt from type-erasure, e.g. at runtime instead of Array[_] there is a difference between Array[Int], Array[String] and Array[AnyRef] for example. Unlike Java, Scala can handle this mostly transparently, so
class Foo {
val foo = new Array[Int](123)
}
has a direct byte-code invocation for creating the integer array, whereas
class Bar[A](implicit ev: reflect.ClassTag[A]) {
val bar = new Array[A](123)
}
is solved by using the implicit type evidence parameter of type ClassTag[A] so that at runtime the JVM can still create the correct array. This is translated into the call you saw, ev.newArray(123).
I am a newbie to functional programming language and I am learning it in Scala for a University project.
This may seem simple, but I am unable to find enough help online for this or a straightforward way of doing this - how can I convert an Array[String] to Array[Double]? I have a CSV file which, when read into the REPL is interpreted as String values (each line of the file has a mix of integer and string values) which would return a type Array[String]. I want to encode the string values with a double/int values to return Array[Double] in order to make the array homogeneous. Is there a straightforward way of doing this? Any guidance will be much appreciated.
What I have done until now is:
def retrieveExamplesFromFile(fileName : String) : Array[Array[String]] = {
val items = for {
line <- Source.fromFile(fileName).getLines()
entries = line.split(",")
} yield entries
return items.toArray
}
The format of each line (returned as String[]) is so:
[[1.0, 2.0, item1], [5, 8.9, item2],....]
And to convert each line in the CSV file into double array, I only have a psuedo definition drafted so:
def generateNumbersForStringValues(values : Array[String]) : Array[Double] = {
val line = for(item <- values)
{
//correct way?
item.replace("item1", "1.0")
item.replace("item2", "1.0")
}
return //unable to typecast/convert
}
Any ideas are welcome. Thank you for your time.
You probably want to use map along with toDouble:
values.map(x => x.toDouble)
Or more concisely:
values.map(_.toDouble)
And for the fallback for non-double strings, you might consider using the Try monad (in scala.util):
values.map(x => Try(x.toDouble).getOrElse(1.0))
If you know what each line will look like, you could also do pattern matching:
values map {
case Array(a, b, c) => Array(a.toDouble, b.toDouble, 1.0)
}
Expanding on #DaunnC's comment, you can use the Try utility to do this and pattern match on the result so you can avoid calling get or wrapping your result in an Option:
import scala.util.{Try, Success, Failure}
def main = {
val maybeDoubles = Array("5", "1.0", "8.5", "10.0", "item1", "item2")
val convertDoubles = maybeDoubles.map { x =>
Try(x.toDouble)
}
val convertedArray = convertDoubles.map {
_ match {
case Success(res) => res
case Failure(f) => 1.0
}
}
convertedArray
}
This allows you to pattern match on the result of Try, which is always either a Success or Failure, without having to call get or otherwise wrap your results.
Here is some more information on Try courtesy of Mauricio Linhares: https://mauricio.github.io/2014/02/17/scala-either-try-and-the-m-word.html
You mean to convert all strings to double with a fallback to 1.0 for all inconvertible strings? That would be:
val x = Array(
Array("1.0", "2.0", "item1"),
Array("5", "8.9", "item2"))
x.map( _.map { y =>
try {
y.toDouble
} catch {
case _: NumberFormatException => 1.0
}
})
Scala 2.13 introduced String::toDoubleOption which used within a map transformation, can be associated with Option::getOrElse to safely cast Strings into Doubles:
Array("5", "1.0", ".", "8.5", "int").map(_.toDoubleOption.getOrElse(1d))
// Array[Double] = Array(5.0, 1.0, 1.0, 8.5, 1.0)