Spock framework contains #Unroll annotation which results in showing each 'case' from parametrized test as a separate test. Is something similar possible with ScalaTest?
A code snippet from the internet which worked for me:
import org.scalatest.FreeSpec
import org.scalatest._
import org.scalatest.prop.TableDrivenPropertyChecks
class OrderValidationTableDrivenSpec extends FreeSpec
with TableDrivenPropertyChecks
with Matchers {
"Order Validation" - {
"should validate and return false if" - {
val orders = Table(
("statement" , "order")
, ("price is negative" , Order(quantity = 10, price = -2))
, ("quantity is negative" , Order(quantity = -10, price = 2))
, ("price and quantity are negative" , Order(quantity = -10, price = -2))
)
forAll(orders) {(statement, invalidOrder) =>
s"$statement" in {
OrderValidation.validateOrder(invalidOrder) shouldBe false
}
}
}
}
}
Imports and class naming may be different depends on scalatest version.
The closest would be the table-driven property checks:
import org.scalatest.prop.TableDrivenPropertyChecks._
val fractions =
Table(
("n", "d"), // First tuple defines column names
( 1, 2), // Subsequent tuples define the data
( -1, 2),
( 1, -2),
( -1, -2),
( 3, 1),
( -3, 1),
( -3, 0),
( 3, -1),
( 3, Integer.MIN_VALUE),
(Integer.MIN_VALUE, 3),
( -3, -1)
)
import org.scalatest.matchers.ShouldMatchers._
forAll (fractions) { (n: Int, d: Int) =>
whenever (d != 0 && d != Integer.MIN_VALUE
&& n != Integer.MIN_VALUE) {
val f = new Fraction(n, d)
if (n < 0 && d < 0 || n > 0 && d > 0)
f.numer should be > 0
else if (n != 0)
f.numer should be < 0
else
f.numer should be === 0
f.denom should be > 0
}
}
There are other techniques such as "sharing tests" and
more property-based testing.
Related
Given an array of integers and an integer k, find out whether there are two distinct indices i and j in the array such that nums[i] = nums[j] and the absolute difference between i and j is at most k.
Example 1:
Input: nums = [1,2,3,1], k = 3
Output: true
Example 2:
Input: nums = [1,0,1,1], k = 1
Output: true
object Solution {
def containsNearbyDuplicate(nums: Array[Int], k: Int): Boolean = {
for (i <- 0 until nums.length - 2) {
for (j <- i until nums.length - 1) {
if (nums(i) == nums(j) && (j - i == k)) return true
}
}
return false
}
}
Your input
[1,2,3,1]
3
Output
false
Expected
true
I don't think you're evaluating the full length of the inner list.
As written, j will never equal 3.
I think if you make your inner loop read for (j <- i until nums.length) that should work.
Probably want to fix the outer loop too. Otherwise, i will never reach the value of 3 stored in your array.
Using until will exclude, so if you use i <- 0 until nums.length -1 where length is 3, then the value of i will be 0, 1, 2 and end.
If you want to subtract - 1 because it feels easier or makes more sense to you, then you probably want to use i <- 0 to nums.length - 1.
Otherwise, i <-0 until nums.length will result in the value of i being 0, 1, 2, 3 as it iterates through the for loop.
Hope that helps.
This is functional, efficient as O(n) instead of O(n^2) and fixes a bug(i != j). You should always have a negative case too when testing ;)
import math.{min, max}
def containsNearbyDuplicate(nums: Array[Int])(k: Int): Boolean = {
val indexesToCheck = for {
i <- 0 until nums.length
j <- max(0, i - k) to min(nums.length - 1, i + k)
if i != j
} yield (i, j)
indexesToCheck.exists {case (i, j) => nums(i) == nums(j)}
}
containsNearbyDuplicate(nums = Array(1, 2, 3, 1, 5))(k = 3)
containsNearbyDuplicate(nums = Array(1, 2, 3, 1, 5))(k = 2)
containsNearbyDuplicate(nums = Array(1, 0, 1, 1, 5))(k = 1)
Trying to remove the first element of a list if it is zero (not really, but for the purpose of an example).
Given a list:
val ns = List(0, 1, 2)
Deleting the first zero could be done by dropping the first matches for zero:
List(0, 1, 2).dropWhile(_ == 0)
res1: List[Int] = List(1, 2)
Or you could delete everything that's not a zero.
List(0, 1, 2).filter(_ > 0)
res2: List[Int] = List(1, 2)
The problem with these is when the list has multiple zeroes. The previous solutions don't work, because they delete too many zeroes:
List(0, 0, 1, 2, 0).filter(_ > 0)
res3: List[Int] = List(1, 2)
List(0, 0, 1, 2, 0).dropWhile(_ == 0)
res4: List[Int] = List(1, 2, 0)
Is there an existing function for this?
I also think pattern matching is the best option for readability and performance (I tested and the pattern matching code from OP actually performs better than simple if ... else ....).
List(0, 0, 1, 2, 0) match {
case 0 :: xs => xs
case xs => xs
}
res10: List[Int] = List(0, 1, 2, 0)
And, no, there's no simple built in function for this.
If you only want to drop the first element conditionally, then as jwvh commented, an if/else comprehension is probably the simplest:
if (ns.nonEmpty && ns.head == 0) {
ns.tail
} else {
ns
}
You could then of course wrap this into a function.
You could look for a sequence of one zero, and drop it:
if (ns.startsWith(List(0))) {
ns.drop(1)
} else {
ns
}
Also known as returning the tail:
if (ns.startsWith(List(0))) {
ns.tail
} else {
ns
}
A neat generalized solution would be explicitly add information to your elements.
Example:
How to drop by condition and limit the amount from left to right?
List(0,0,0,1,2,2,3).zipWithIndex.dropWhile({case (elem,index) => elem == 0 && index < 2})
Result:
res0: List[(Int, Int)] = List((0,2), (1,3), (2,4), (2,5), (3,6))
You can get your previous representation with:
res0.map.{_._1}
To do everything in N, you can use lazy evaluation + the force method.
List(0,0,0,1,2,2,3).view.zipWithIndex.dropWhile({case (elem,index) => elem == 0 && index < 2}).map {_._1}.force
This will basically do all the operations on your initial collection in one single iteration. See scaladoc for more info to Scala views..
Modifying your condition on the right size you can choose how far the drop condition will reach inside your collection.
Here's a generalised variant (drop up to K elements matching the predicate) which does not process the rest of the list
def dropWhileLimit[A](xs: List[A], f: A => Boolean, k: Int): List[A] = {
if (k <= 0 || xs.isEmpty || !f(xs.head)) xs
else dropWhileLimit(xs.tail, f, k - 1)
}
and some test cases:
dropWhileLimit(List(0,1,2,3,4), { x:Int => x == 0}, 1)
//> res0: List[Int] = List(1, 2, 3, 4)
dropWhileLimit(List(0,1,2,3,4), { x:Int => x == 0}, 2)
//> res1: List[Int] = List(1, 2, 3, 4)
dropWhileLimit(List(0,0,0,0,0), { x:Int => x == 0}, 1)
//> res2: List[Int] = List(0, 0, 0, 0)
dropWhileLimit(List(0,0,0,0,0), { x:Int => x == 0}, 3)
//> res3: List[Int] = List(0, 0)
You can zip the list with an index:
ns.zipWithIndex.filter( x =>( x._1 != 0 || x._2 != 0)).map(_._1)
Here's a similar solution using dropWhile:
ns.zipWithIndex.dropWhile {
case (x, idx) => x == 0 && idx == 0
} map(_._1)
This could also be a for-comprehension
for {
(x, idx) <- ns.zipWithIndex
if (x != 0 || idx != 0) )
} yield {
x
}
But as Paul mentioned, it will unnecessarily iterate over the entire list.
That's a good option if you wanna drop the first element according to the filter but the value isn't in the first place.
def dropFirstElement( seq: Seq[Int], filterValue: Int ): Seq[Int] = {
seq.headOption match {
case None => seq
case Some( `filterValue` ) => seq.tail
case Some( value ) => value +: dropFirstElement( seq.tail, filterValue )
}
}
I have been trying to create code that counts the max substring from array and returns sum and starting and ending point. I have some error in there however that I cannot spot because for example when I use Array(-2, 1, -3, 4, -1, 2, 1, -5, 4) as source I get return (7, 5,8). However, the correct answer should be (6, 3, 6). Code below.
def solve(a: Array[Int]): (Int, Int, Int) = {
require(a.nonEmpty)
val n = a.length
val temp = Array.fill(n)(0, 0, 0)
var max = (0,0,0) // sum, start, end
for (i <- 0 to n-1) {
temp(i) = (a(i), i, i)
}
for (i <- 0 to n-1) {
for (j <- 0 to i) {
if (a(i) > a(j) && temp(i)._1 < temp (j)._1 + a(i)) {
temp(i) = (temp(j)._1 + a(i), j, i)
}
}
}
for (i <- 0 to n-1){
if (max._1 < temp(i)._1){
max = temp(i)
}
}
return max
}
How about a more Scala/functional approach?
def solve(a: Array[Int]): (Int, Int, Int) = {
a.tails.zipWithIndex.flatMap{
case (arr, ti) =>
arr.inits.map{
tail => (tail.sum, ti, ti + tail.length -1)
}
}.maxBy(_._1)
}
solve (Array(-2, 1, -3, 4, -1, 2, 1, -5, 4)) => res7: (Int, Int, Int) = (6,3,6)
I'm trying out ScalaCheck and haven't been able to figure out how to build the first example on the ScalaTest user's manual. I'm pretty sure that the following needs to be preceded by some imports and wrapped inside a class that extends from some library class. The page in the user's manual says to "mix in" PropertyChecks, but it doesn't give an example of an appropriate class to mix it into:
forAll { (n: Int, d: Int) =>
whenever (d != 0 && d != Integer.MIN_VALUE
&& n != Integer.MIN_VALUE) {
val f = new Fraction(n, d)
if (n < 0 && d < 0 || n > 0 && d > 0)
f.numer should be > 0
else if (n != 0)
f.numer should be < 0
else
f.numer should be === 0
f.denom should be > 0
}
}
I've been trying various combinations, but the best result I've gotten so far is compilation errors like this:
[info] Compiling 1 Scala source to .../target/scala-2.11/test-classes...
[error] .../src/test/scala/TestFraction.scala:14: value should is not a member of Int
[error] f.numer should be > 0
[error] ^
What would be a complete source file for the test, imports and all?
If you check the github repository of scala test here you can find some extra code here
github source scalatest
For your source this are the imports and the structure
import org.scalatest.{Matchers, FlatSpec}
import org.scalatest.prop.PropertyChecks
class Fraction(n: Int, d: Int) {
require(d != 0)
require(d != Integer.MIN_VALUE)
require(n != Integer.MIN_VALUE)
val numer = if (d < 0) -1 * n else n
val denom = d.abs
override def toString = numer + " / " + denom
}
class PropertySpec extends FlatSpec with PropertyChecks with Matchers {
forAll { (n: Int, d: Int) =>
whenever(d != 0 && d != Integer.MIN_VALUE
&& n != Integer.MIN_VALUE) {
val f = new Fraction(n, d)
if (n < 0 && d < 0 || n > 0 && d > 0)
f.numer should be > 0
else if (n != 0)
f.numer should be < 0
else
f.numer should be === 0
f.denom should be > 0
}
}
val invalidCombos =
Table(
("n", "d"),
(Integer.MIN_VALUE, Integer.MIN_VALUE),
(1, Integer.MIN_VALUE),
(Integer.MIN_VALUE, 1),
(Integer.MIN_VALUE, 0),
(1, 0)
)
forAll(invalidCombos) { (n: Int, d: Int) =>
evaluating {
new Fraction(n, d)
} should produce[IllegalArgumentException]
}
}
Recent versions of scalatest do not implementevaluating and mark === as obsolete.
So, I'd rewrite example as:
import org.scalatest._
import org.scalatest.prop.PropertyChecks
protected class Fraction(n: Int, d: Int) {
require(d != 0 && d != Integer.MIN_VALUE && n != Integer.MIN_VALUE)
val numer = if (d < 0) -1 * n else n
val denom = d.abs
override def toString = numer + " / " + denom
}
class SscceProps extends FlatSpec with PropertyChecks with Matchers {
it should "valid combos" in { forAll { (n: Int, d: Int) =>
whenever(d != 0 && d != Integer.MIN_VALUE && n != Integer.MIN_VALUE) {
val f = new Fraction(n, d)
if (n < 0 && d < 0 || n > 0 && d > 0) f.numer should be > 0
else if (n != 0) f.numer should be < 0
else f.numer shouldBe 0
f.denom should be > 0
}
}}
it should "invalid combos" in {
forAll(Table(
("n", "d"),
(Integer.MIN_VALUE, Integer.MIN_VALUE),
(1, Integer.MIN_VALUE),
(Integer.MIN_VALUE, 1),
(Integer.MIN_VALUE, 0),
(1, 0)
)) { (n: Int, d: Int) =>
an[IllegalArgumentException] should be thrownBy {new Fraction(n, d)}}
}
}
Assuming there the list of numbers and a range value, I want to group them into groups, in which the item in each group is within the range from the lowest number, and sort them.
For example, I have a list val l = List(1,2,3,4,5,6,7,8,9,10) and the range val range = 2. Then, I'm looking for a way to output the following result: result = List(List(1,2,3), List(4,5,6), List(7,8,9), List(10)). Which means if range = 0 then only identical numbers are in the same group.
At the moment, I use the following method
val minVal = l.min
val range1 = (minVal + range).toDouble
val groups = l.foldLeft(Map[Int, List[Int]]())((result, num) => {
val numRange = math.ceil(num / range1).toInt
if (result.contains(numRange)) {
result.updated(numRange, num :: result(numRange))
} else {
result.updated(numRange, List(num))
}
})
groups.keys.toList.sortBy(k => k).map(groups(_))
It works in most cases except when range = 0 and slowestNum != 1. E.g. for the list val l = List(2,3,4,5,6,7,8,9,10) and the range val range = 2, the result is List(List(2), List(4, 3), List(6, 5), List(8, 7), List(10, 9)).
So, I wonder if there is any other way to solve this problem.
Why complicate?
def coll(l: List[Int], range: Int): List[List[Int]] =
if (l.isEmpty) Nil else {
val (b, a) = l.span((l.head - range to l.head + range).contains)
b :: coll(a, range)
}
So, this algorithm collects numbers into a group until the number are in the plus/minus range.
val list = List(7,4,1,9,10,20,50,52,30)
coll(list, 3)
res6: List[List[Int]] = List(List(7, 4), List(1), List(9, 10), List(20), List(50, 52), List(30))
If you want each group by itself sorted, then call res6.map(_.sorted).
I would personally do something like this:
val l = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
val range = 2
val result = l.sorted.foldLeft(List[List[Int]]()) {
(cur, x) =>
if ((cur nonEmpty) && x - cur.head.last <= range) (x :: cur.head) :: cur.tail
else List(x) :: cur
}
although there may be some clever and neat ways. Of course, you can always do if you want the result ordered:
val ret = result.reverse.map(_.reverse)
Hope it helped!
Try something like this
val groupedList = l.map(i => l.filter(s => s >= i && s - i <= range))
groupedList.foldLeft(List(groupedList.head)) {
case (r, c) => if (r.last.last < c.head) r ++ List(c) else r
}
For range 2
val l = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
val range = 2
val groupedList = l.map(i => l.filter(s => s >= i && s - i <= range))
groupedList.foldLeft(List(groupedList.head)) {
case (r, c) => if (r.last.last < c.head) r ++ List(c) else r
}
//> res0: List[List[Int]] = List(List(1, 2, 3), List(4, 5, 6), List(7, 8, 9), List(10))
For range 0
val l = List(1,1,1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
val range = 0
val groupedList = l.map(i => l.filter(s => s >= i && s - i <= range))
groupedList.foldLeft(List(groupedList.head)) {
case (r, c) => if (r.last.last < c.head) r ++ List(c) else r
}
//> res0: List[List[Int]] = List(List(1, 1, 1), List(2), List(3), List(4), List(5), List(6), List(7), List(8), List(9), List(10))