Scala - conditionally sum elements in list - scala

I am trying to solve a beginner problem with lists but can't find an example to help me get it work. I am given a list of positive and negative integers (AccountHistory) and I need to check if the negative integers in this list have ever exceeded -1000. I expected my code to work with a freshly introduced helper function like this:
def checkAccount(account: AccountHistory): Boolean = {
def helper(i: AccountHistory): Int = {
var total = 0
i.collect{case x if x < 0 => Math.abs(x) + total}
return total
}
if (helper(account) >1000) true else false
}
But it doesn't work. Please help me find my mistake or problem in wrong approach.
Edit: The pre-given tests include
assert(checkAccount(List(10,-5,20)))
assert(!checkAccount(List(-1000,-1)))
So if assert expects true then my approach is wrong to solve it like this.
By 'exceeded' I mean <-1000, for any or all elements in a list (like exceeding a credit amount in given period).

i.collect{case x if x < 0 => Math.abs(x) + total}
In the above code snippet, not assign back to total, maybe you need:
val total = i.filter(_ < 0).map(Math.abs).sum

I think this is what you're supposed to do:
def checkAccount(account: AccountHistory): Boolean =
account.forall(_ > -1000)

Related

"Find the Parity Outlier Code Wars (Scala)"

I am doing some of CodeWars challenges recently and I've got a problem with this one.
"You are given an array (which will have a length of at least 3, but could be very large) containing integers. The array is either entirely comprised of odd integers or entirely comprised of even integers except for a single integer N. Write a method that takes the array as an argument and returns this "outlier" N."
I've looked at some solutions, that are already on our website, but I want to solve the problem using my own approach.
The main problem in my code, seems to be that it ignores negative numbers even though I've implemented Math.abs() method in scala.
If you have an idea how to get around it, that is more than welcome.
Thanks a lot
object Parity {
var even = 0
var odd = 0
var result = 0
def findOutlier(integers: List[Int]): Int = {
for (y <- 0 until integers.length) {
if (Math.abs(integers(y)) % 2 == 0)
even += 1
else
odd += 1
}
if (even == 1) {
for (y <- 0 until integers.length) {
if (Math.abs(integers(y)) % 2 == 0)
result = integers(y)
}
} else {
for (y <- 0 until integers.length) {
if (Math.abs(integers(y)) % 2 != 0)
result = integers(y)
}
}
result
}
Your code handles negative numbers just fine. The problem is that you rely on mutable sate, which leaks between runs of your code. Your code behaves as follows:
val l = List(1,3,5,6,7)
println(Parity.findOutlier(l)) //6
println(Parity.findOutlier(l)) //7
println(Parity.findOutlier(l)) //7
The first run is correct. However, when you run it the second time, even, odd, and result all have the values from your previous run still in them. If you define them inside of your findOutlier method instead of in the Parity object, then your code gives correct results.
Additionally, I highly recommend reading over the methods available to a Scala List. You should almost never need to loop through a List like that, and there are a number of much more concise solutions to the problem. Mutable var's are also a pretty big red flag in Scala code, as are excessive if statements.

Absolute function

In my program I am using Bigdecimal to truncate numbers and storing them in a variable. Eg. 123.456789 is getting displayed as 123.45.Further I am trying to find the absolute of the numbers.The problem arises here i.e - 123.45 should appear as 123.45 but it's appearing as 123.4589Egh.Can someone please help as to how can I find absolute of numbers.
var diff1=BigDecimal(diff).setScale(2, BigDecimal.RoundingMode.HALF_UP).toDouble
var bigdec=abs(diff1)
Try taking inputs for 10-15 numbers in an array (in diff variable)
Uhm, I'm not sure what your problem is, but for me this works fine:
val diff = -123.456789
var diff1 = BigDecimal(diff).setScale(2, BigDecimal.RoundingMode.DOWN).toDouble
var bigdec = Math.abs(diff1)
println(bigdec) // 123.45
Note that if you want 123.45 instead of 123.46 you have to change your rounding mode.
Taking in an array doesn't change anything, although you need to make a def and map over the array now when rounding - as you cannot call the BigDecimal apply function on an Array:
// generates an Array of 20 elements with random doubles from 0 to 200
val diff = Array.fill(20)(math.random).map(_ * 200)
.map { num => // using this map function to make some negatives
if (num < 100) num * -1
else num
}
def round(double: Double) = BigDecimal(double)
.setScale(2, BigDecimal.RoundingMode.HALF_UP)
.toDouble
var absolute = diff.map(num => Math.abs(round(num)))
Does the above code reflect what you are doing? If so, for var absolute I am getting an Array[Double] with positive numbers and only 2 decimal places.

Variable not updated

This is perhaps very basic but I am certainly missing something here.
It is all within a method, where I increment/alter a variable within a child/sub scope (it could be within a if block, or like here, within a map)
However, the result is unchanged variable. e.g. here, sum remains zero after the map. whereas, it should come up to 3L.
What am I missing ?
val resultsMap = scala.collection.mutable.Map.empty[String, Long]
resultsMap("0001") = 0L
resultsMap("0003") = 2L
resultsMap("0007") = 1L
var sum = 0L
resultsMap.mapValues(x => {sum = sum + x})
// I first wrote this, but then got worried and wrote more explicit version too, same behaviour
// resultMap.mapValues(sum+=_)
println("total of counts for txn ="+sum) // sum still 0
-- Update
I have similar behaviour where a loop is not updating the variable outside the loop. looking for text on variable scoping, but not found the golden source yet. all help is appreciated.
var cnt : Int = 0
rdd.foreach(article => {
if (<something>) {
println(<something>) // being printed
cnt += 1
println("counter is now "+cnt) // printed correctly
}
})
You should proceed like this:
val sum = resultsMap.values.reduce(_+_)
You just get your values and then add them up with reduce.
EDIT:
The reason sum stays unchanged is that mapValues produces a view, which means (among other things) the new map won't be computed unless the resulting view is acted upon, so in this case - the code block updating sum is simply never executed.
To see this - you can "force" the view to "materialize" (compute the new map) and see that sum is updated, as expected:
var sum = 0L
resultsMap.mapValues(x => {sum = sum + x}).view.force
println("SUM: " + sum) // prints 3
See related discussion here: Scala: Why mapValues produces a view and is there any stable alternatives?

Fast Fibonacci using recursion

I implemented a Fibonacci function in Scala and it works fine however when I enter 50 it takes a long time to compute it because it has to calculate the 2 previous integers each time. I found a function that keeps the 2 previous numbers. However, can somebody tell me how to write this function to make it accept 2 integers instead of 3 and return the last 2 numbers to compute the Fibonacci at a particular index x. Thanks!
def fastFib(x: Long ): Long = {
def fast(x:Long , a:Long, b:Long):Long =
if (x<=0) a+b
else fast(x-1,b,a+b)
if (x<2) 1
else fast(x-2,0,1)
}
You can cache the intermediate results, then you never recompute the same result twice
Here is the code
//this supposed to contains all the value when initialized
//initialized with 0 for all value
val cache = Array [Int] (101);//0 to 100
cache(1)==1;//initial value
cache(2)=1;//initial value
def fibonacciCache(n:Int) : Int = {
if (n>100)
{
println("error");
return -1;
}
if (cache(n)!=0)//means value has been calculated
return cache(n);
else
{
cache(n)=fibonacciCache(n-1)+fibonacciCache(n-2);
return cache(n);
}
}
Hope that helps

How to generate a list of random numbers?

This might be the least important Scala question ever, but it's bothering me. How would I generate a list of n random number. What I have so far:
def n_rands(n : Int) = {
val r = new scala.util.Random
1 to n map { _ => r.nextInt(100) }
}
Which works, but doesn't look very Scalarific to me. I'm open to suggestions.
EDIT
Not because it's relevant so much as it's amusing and obvious in retrospect, the following looks like it works:
1 to 20 map r.nextInt
But the index of each entry in the returned list is also the upper bound of that last. The first number must be less than 1, the second less than 2, and so on. I ran it three or four times and noticed "Hmmm, the result always starts with 0..."
You can either use Don's solution or:
Seq.fill(n)(Random.nextInt)
Note that you don't need to create a new Random object, you can use the default companion object Random, as stated above.
How about:
import util.Random.nextInt
Stream.continually(nextInt(100)).take(10)
regarding your EDIT,
nextInt can take an Int argument as an upper bound for the random number, so 1 to 20 map r.nextInt is the same as 1 to 20 map (i => r.nextInt(i)), rather than a more useful compilation error.
1 to 20 map (_ => r.nextInt(100)) does what you intended. But it's better to use Seq.fill since that more accurately represents what you're doing.