Scala - Perfect Number usage in higher order function - anything wrong? - scala

Apologies , but am new to scala... learning it, now.
I have been trying to complete a excercise where the ask was as follows :-
// Write a function isPerfectNumber which takes integer input and returns String output.
// It finds if a number is perfect, and returns true if perfect, else returns false
// Write a higher order function myHigherOrderFunction which takes isPerfectNumber and intList as input, and returns a List of Strings which contain the output if the number is perfect or not using map.
Perfect Number :
https://rosettacode.org/wiki/Perfect_numbers
just go to the scala section
My Code :
object ListMapHigherOrder{
def main(args:Array[String])
{
val intRes = args.toList
val intList: List[Int] = intRes.map(_.toInt).toList
def isPerfectNumber(input: Int) :String =
{
var check_sum = ( (2 to math.sqrt(input).toInt).collect { case x if input % x == 0 => x + input / x} ).sum
if ( check_sum == input - 1 )
return "true"
else
return "false"
}
def myHigherOrderFunction(argFn: Int => String, argVal:List[Int]): List[String] = { argVal.map(argFn) }
println(myHigherOrderFunction(isPerfectNumber, intList))
}
}
Code execution : scala ScalaExcercise12.scala 1 6 13
Expected Output : List(false , true , false)
the code gives expected output, am not sure how the backend testing is being done.... it just dosent pass the test.
Is there any issue with the code? - i did like to fix it , but cant i see anything wrong/missing especially because i am getting the same output as desired :(

object ListMapHigherOrder{
def main(args:Array[String])
{
val intRes = args.toList
val intList: List[Int] = intRes.map(x=>x.toInt)
def isPerfectNumber(input: Int) :String =
{
var sum = 0
for(i <- 1 until input){
if(input % i == 0)
sum = sum+i
}
if ( sum == input )
return "true"
else
return "false"
}
def myHigherOrderFunction(argFn: Int => String, argVal:List[Int]): List[String] = { argVal.map(argFn) }
println(myHigherOrderFunction(isPerfectNumber, intList))
}
}
**This worked for me

Related

Cleaner way to find all indices of same value in Scala

I have a textfile like so
NameOne,2,3,3
NameTwo,1,0,2
I want to find the indices of the max value in each line in Scala. So the output of this would be
NameOne,1,2
NameTwo,2
I'm currently using the function below to do this but I can't seem to find a simple way to do this without a for loop and I'm wondering if there is a better method out there.
def findIndices(movieRatings: String): (String) = {
val tokens = movieRatings.split(",", -1)
val movie = tokens(0)
val ratings = tokens.slice(1, tokens.size)
val max = ratings.max
var indices = ArrayBuffer[Int]()
for (i<-0 until ratings.length) {
if (ratings(i) == max) {
indices += (i+1)
}
}
return movie + "," + indices.mkString(",")
}
This function is called as so:
val output = textFile.map(findIndices).saveAsTextFile(args(1))
Just starting to learn Scala so any advice would help!
You can zipWithIndex and use filter:
ratings.zipWithIndex
.filter { case(_, value) => value == max }
.map { case(index, _) => index }
I noticed that your code doesn't actually produce the expected result from your example input. I'm going to assume that the example given is the correct result.
def findIndices(movieRatings :String) :String = {
val Array(movie, ratings #_*) = movieRatings.split(",", -1)
val mx = ratings.maxOption //Scala 2.13.x
ratings.indices
.filter(x => mx.contains(ratings(x)))
.mkString(s"$movie,",",","")
}
Note that this doesn't address some of the shortcomings of your algorithm:
No comma allowed in movie name.
Only works for ratings from 0 to 9. No spaces allowed.
testing:
List("AA"
,"BB,"
,"CC,5"
,"DD,2,5"
,"EE,2,5, 9,11,5"
,"a,b,2,7").map(findIndices)
//res0: List[String] = List(AA, <-no ratings
// , BB,0 <-comma, no ratings
// , CC,0 <-one rating
// , DD,1 <-two ratings
// , EE,1,4 <-" 9" and "11" under valued
// , a,0 <-comma in name error
// )

How to use fold to do boolean testing

I'd like to know the idiomatic way to approach this problem in scala.
Given a start date and an end date and a collection of dates in between, determine whether the given collection of dates contains all the dates necessary to go from the start date to the end date with no gap dates in between.
Type signature:
def checkDate(start: DateTime, end: DateTime, between: IndexedSeq[DateTime]): Boolean
The "normal" or "not functional" way to do this would be something like this:
def checkDate(start: DateTime, end: DateTime, between: IndexedSeq[DateTime]): Boolean = {
i = 1
status = true
while(start != end) {
d = start.plusDays(i)
if (!between.contains(d) {
status = false
break
}
i += 1
}
return status
}
How can I do this using a Fold?
Here's my thought process so far:
def checkDate(start: DateTime, end: DateTime, between: IndexedSeq[DateTime]): Boolean = {
// A fold will assume the dates are in order and move left (or right)
// This means the dates must be sorted.
val sorted = between.sortBy(_.getMillis())
val a = sorted.foldLeft(List[Boolean]) {
(acc, current) => {
// How do I access an iterable version of the start date?
if (current == ??) {
acc :: true
} else false
}
}
// If the foldLeft produced any values that could NOT be matched
// to the between list, then the start date does not have an
// uninterrupted path to the end date.
if (a.count(_ == false) > 0) false
else true
}
I just need to figure out how to index the start parameter so I can increase the value of it as the fold iterates over the between collection. Or it's possible that fold isn't what I'm supposed to use at all.
Any help would be appreciated!
You can pass previous DateTime item in accumulator:
val a = sortedBetween.foldLeft((List[Boolean](), start)) {
case ((results, prev), current) => {
... calculate res here ...
(results ++ List(res), current)
}
}
But for this kind of check you better use sliding and forall combination:
sortedBetween.sliding(2).forall {
case List(prev,cur) => ..do the check here ..
}
Also, note that you ingnoring the result of between sorting since IndexedSeq is immutable. Fix - use another val:
val sortedBetween = between.sortBy(_.getMillis())
I think a fold isn't necessary, it's making things too hard.
Suppose you had the following functions:
private def normalizeDateTime( dt : DateTime ) : DateMidnight = ???
private def requiredBetweens( start : DateMidnight, end : DateMidnight ) : Seq[DateMidnight] = ???
Then you could write your function as follows:
def checkDate(start: DateTime, end: DateTime, between: IndexedSeq[DateTime]): Boolean = {
val startDay = normalizeDateTime( start )
val endDay = normalizeDateTime( end )
val available = between.map( normalizeDateTime ).toSet
val required = requiredBetweens( startDay, endDay ).toSet
val unavailable = (required -- available)
unavailable.isEmpty
}
Note that this function imposes no requirement as to the ordering of the betweens, treats the elements as a Set, only requiring that each day be available somewhere.
To implement normalizeDateTime(...) you might get away with something as simple as dt.toDateMidnight, but you should think a bit about Chronology and time zone issues. It's critical that DateTime objects that you mean to represent a day always normalize to the same DateMidnight.
To implement requiredBetweens(...), you might consider using a Stream and takeWhile(...) for an elegant solution. You might want to require that (end isAfter start).
I would use filter and then zip and take the difference, the dates should always be one day apart, so check they are all 1.
# val ls = Array(1, 2, 3, 4, 5, 6, 7) // can use dates in the same way
ls: Array[Int] = Array(1, 2, 3, 4, 5, 6, 7)
# val ls2 = ls.filter { i => (2 < i) && (i < 6) }
ls2: Array[Int] = Array(3, 4, 5)
# ls2.zip(ls2.drop(1))
res21: Array[(Int, Int)] = Array((3, 4), (4, 5))
# ls2.zip(ls2.drop(1)).map { case (x, y) => y-x }
res22: Array[Int] = Array(1, 1)
# ls2.zip(ls2.drop(1)).map { case (x, y) => y-x }.forall { _ == 1 }
res23: Boolean = true
You also have to check that no dates are missing:
# ls2.length == 6 - 2 - 1 // beware off-by-one errors
res25: Boolean = true
You may also be able to do this more simply by using the Range object:
# ls2.zipAll(3 to 5 by 1, 0, 0).forall { case (x, y) => x == y }
res46: Boolean = true
This should work, but may need a slight tweak for DateTime...
# val today = LocalDate.now
today: LocalDate = 2017-10-19
# val a = (0 to 9).reverse.map { today.minusDays(_) }
a: collection.immutable.IndexedSeq[LocalDate] = Vector(2017-10-10, 2017-10-11, 2017-10-12, 2017-10-13, 2017-10-14, 2017-10-15, 2017-10-16, 2017-10-17, 2017-10-18, 2017-10-19)
# a.zip(a.drop(1)).map { case (x, y) => x.until(y) }.forall { _ == Period.ofDays(1) }
res71: Boolean = true
Solution with tail recursion. I'm using ZonedDateTime from Java 8 for DateTime representation. Here is online version on codepad.remoteinterview.io:
import scala.annotation.tailrec
import java.time.ZonedDateTime
object TailRecursionExample {
def checkDate(start: ZonedDateTime, end: ZonedDateTime,
between: Seq[ZonedDateTime]): Boolean = {
// We have dates in range (inclusive) [start, end] with step = 1 day
// All these days should be in between collection
// set for fast lookup
val set = between.toSet
#tailrec
def checkDate(curr: ZonedDateTime, iterations: Int): (Int, Boolean) = {
if (curr.isAfter(end)) (iterations, true)
else if (set.contains(curr)) checkDate(curr.plusDays(1), iterations + 1)
else (iterations, false)
}
val (iterations, result) = if (start.isAfter(end))
(0, false)
else
checkDate(start, 0)
println(s"\tNum of iterations: $iterations")
result
}
def main(args: Array[String]): Unit = {
testWhenStartIsAfterEnd()
println
testWhenStartIsBeforeEnd()
println
testWhenStartIsBeforeEndButBetweenSkipOneDay()
println
()
}
def testWhenStartIsAfterEnd(): Unit = {
val start = ZonedDateTime.now().plusDays(5)
val end = ZonedDateTime.now()
val between = (0 to 5).map(i => start.plusDays(i))
verboseTest("testWhenStartIsAfterEnd", start, end, between)
}
def testWhenStartIsBeforeEnd(): Unit = {
val start = ZonedDateTime.now().minusDays(5)
val end = ZonedDateTime.now()
val between = (0 to 5).map(i => start.plusDays(i))
verboseTest("testWhenStartIsBeforeEnd", start, end, between)
}
def testWhenStartIsBeforeEndButBetweenSkipOneDay(): Unit = {
val start = ZonedDateTime.now().minusDays(5)
val end = ZonedDateTime.now()
val between = (1 to 5).map(i => start.plusDays(i))
verboseTest("testWhenStartIsBeforeEndButBetweenSkipOneDay", start, end, between)
}
def verboseTest(name: String, start: ZonedDateTime, end: ZonedDateTime,
between: Seq[ZonedDateTime]): Unit = {
println(s"$name:")
println(s"\tStart: $start")
println(s"\tEnd: $end")
println(s"\tBetween: ")
between.foreach(t => println(s"\t\t$t"))
println(s"\tcheckDate: ${checkDate(start, end, between)}")
}
}

scala regex multiple integers

I have the following string that I would like to match on: 1-10 employees.
Here is my regex statement val regex = ("\\d+").r
The problem I have is Im trying to find a way to extract the matched data and determine which value returned is bigger.
Here is what IM doing to process it
def setMinAndMaxValue(currentCompany: CurrentCompany, matchIterator: Iterator[Regex.Match]): CurrentCompany = {
var max = 0
println(s"matchIterator - $matchIterator")
matchIterator.collect {
case regex(s: String) => println("found string")
case regex(IntConv(x)) =>
println("regex case")
if (x > max) max = x
}
val (minVal, maxVal) = rangesForMaxValue(max)
val newDetails = currentCompany.details.copy(minSize = Some(minVal), maxSize = Some(maxVal))
currentCompany.copy(details = newDetails)
}
object IntConv {
def unapply(s : String) : Option[Int] = Try {
Some(s.toInt)
}.toOption.flatten
}
I thought I was confused by your original question, then you clarified it with code and now I have no idea what you're trying to do.
To extract numbers from a string, try this.
val re = """(\d+)""".r
val nums = re.findAllIn(string_with_numbers).map(_.toInt).toList
Then you can just nums.min, and nums.max, and whatever number processing you need.

How to deal with decimal number in scala

I have a file like this:
1 4.146846
2 3.201141
3 3.016736
4 2.729412
I want to use toDouble but, it's not working as expected :
val rows = textFile.map { line =>
val fields = line.split("[^\\d]+")
((fields(0),fields(1).toDouble))
}
val Num = rows.sortBy(- _._2).map{case (user , num) => num}.collect.mkString("::")
println(Num)
The result print out is 4.0::3.0::3.0::2.0.
What I expect is 4.146846::3.201141::3.016736::2.729412
How do I do this?
Your regular expression is stopping at the decimal point in 4.146846.
Try line.split("[^\\d.]+")
What about splitting the lines by variant number of whitespaces? The regular expression would be like '[\s]+' . This resumes in two parts per line, one digit and one double string.
My whole program looks like:
object Application {
def parseDouble(s: String) =
try {
Some(s.toDouble)
} catch {
case _ => None
}
def main(args: Array[String]): Unit = {
val linesIt = "1 3.201141\n2 4.146846\n3 3.016736\n4 2.729412".lines
var doubles: List[Double] = List.empty
for (singleLine <- linesIt) {
val oneDouble = parseDouble(singleLine.split("[\\s]+")(1))
doubles = if (oneDouble != None)
oneDouble.get::doubles
else
doubles
}
val doublesArr = doubles.toArray
println("before sorting: " + doublesArr.mkString("::"))
scala.util.Sorting.quickSort(doublesArr)
println("after sorting: " + doublesArr.mkString("::"))
}
}

Palindromes using Scala

I came across this problem from CodeChef. The problem states the following:
A positive integer is called a palindrome if its representation in the
decimal system is the same when read from left to right and from right
to left. For a given positive integer K of not more than 1000000
digits, write the value of the smallest palindrome larger than K to
output.
I can define a isPalindrome method as follows:
def isPalindrome(someNumber:String):Boolean = someNumber.reverse.mkString == someNumber
The problem that I am facing is how do I loop from the initial given number and break and return the first palindrome when the integer satisfies the isPalindrome method? Also, is there a better(efficient) way to write the isPalindrome method?
It will be great to get some guidance here
If you have a number like 123xxx you know, that either xxx has to be below 321 - then the next palindrom is 123321.
Or xxx is above, then the 3 can't be kept, and 124421 has to be the next one.
Here is some code without guarantees, not very elegant, but the case of (multiple) Nines in the middle is a bit hairy (19992):
object Palindrome extends App {
def nextPalindrome (inNumber: String): String = {
val len = inNumber.length ()
if (len == 1 && inNumber (0) != '9')
"" + (inNumber.toInt + 1) else {
val head = inNumber.substring (0, len/2)
val tail = inNumber.reverse.substring (0, len/2)
val h = if (head.length > 0) BigInt (head) else BigInt (0)
val t = if (tail.length > 0) BigInt (tail) else BigInt (0)
if (t < h) {
if (len % 2 == 0) head + (head.reverse)
else inNumber.substring (0, len/2 + 1) + (head.reverse)
} else {
if (len % 2 == 1) {
val s2 = inNumber.substring (0, len/2 + 1) // 4=> 4
val h2 = BigInt (s2) + 1 // 5
nextPalindrome (h2 + (List.fill (len/2) ('0').mkString)) // 5 + ""
} else {
val h = BigInt (head) + 1
h.toString + (h.toString.reverse)
}
}
}
}
def check (in: String, expected: String) = {
if (nextPalindrome (in) == expected)
println ("ok: " + in) else
println (" - fail: " + nextPalindrome (in) + " != " + expected + " for: " + in)
}
//
val nums = List (("12345", "12421"), // f
("123456", "124421"),
("54321", "54345"),
("654321", "654456"),
("19992", "20002"),
("29991", "29992"),
("999", "1001"),
("31", "33"),
("13", "22"),
("9", "11"),
("99", "101"),
("131", "141"),
("3", "4")
)
nums.foreach (n => check (n._1, n._2))
println (nextPalindrome ("123456678901234564579898989891254392051039410809512345667890123456457989898989125439205103941080951234566789012345645798989898912543920510394108095"))
}
I guess it will handle the case of a one-million-digit-Int too.
Doing reverse is not the greatest idea. It's better to start at the beginning and end of the string and iterate and compare element by element. You're wasting time copying the entire String and reversing it even in cases where the first and last element don't match. On something with a million digits, that's going to be a huge waste.
This is a few orders of magnitude faster than reverse for bigger numbers:
def isPalindrome2(someNumber:String):Boolean = {
val len = someNumber.length;
for(i <- 0 until len/2) {
if(someNumber(i) != someNumber(len-i-1)) return false;
}
return true;
}
There's probably even a faster method, based on mirroring the first half of the string. I'll see if I can get that now...
update So this should find the next palindrome in almost constant time. No loops. I just sort of scratched it out, so I'm sure it can be cleaned up.
def nextPalindrome(someNumber:String):String = {
val len = someNumber.length;
if(len==1) return "11";
val half = scala.math.floor(len/2).toInt;
var firstHalf = someNumber.substring(0,half);
var secondHalf = if(len % 2 == 1) {
someNumber.substring(half+1,len);
} else {
someNumber.substring(half,len);
}
if(BigInt(secondHalf) > BigInt(firstHalf.reverse)) {
if(len % 2 == 1) {
firstHalf += someNumber.substring(half, half+1);
firstHalf = (BigInt(firstHalf)+1).toString;
firstHalf + firstHalf.substring(0,firstHalf.length-1).reverse
} else {
firstHalf = (BigInt(firstHalf)+1).toString;
firstHalf + firstHalf.reverse;
}
} else {
if(len % 2 == 1) {
firstHalf + someNumber.substring(half,half+1) + firstHalf.reverse;
} else {
firstHalf + firstHalf.reverse;
}
}
}
This is most general and clear solution that I can achieve:
Edit: got rid of BigInt's, now it takes less than a second to calculate million digits number.
def incStr(num: String) = { // helper method to increment number as String
val idx = num.lastIndexWhere('9'!=, num.length-1)
num.take(idx) + (num.charAt(idx)+1).toChar + "0"*(num.length-idx-1)
}
def palindromeAfter(num: String) = {
val lengthIsOdd = num.length % 2
val halfLength = num.length / 2 + lengthIsOdd
val leftHalf = num.take(halfLength) // first half of number (including central digit)
val rightHalf = num.drop(halfLength - lengthIsOdd) // second half of number (also including central digit)
val (newLeftHalf, newLengthIsOdd) = // we need to calculate first half of new palindrome and whether it's length is odd or even
if (rightHalf.compareTo(leftHalf.reverse) < 0) // simplest case - input number is like 123xxx and xxx < 321
(leftHalf, lengthIsOdd)
else if (leftHalf forall ('9'==)) // special case - if number is like '999...', then next palindrome will be like '10...01' and one digit longer
("1" + "0" * (halfLength - lengthIsOdd), 1 - lengthIsOdd)
else // other cases - increment first half of input number before making palindrome
(incStr(leftHalf), lengthIsOdd)
// now we can create palindrome itself
newLeftHalf + newLeftHalf.dropRight(newLengthIsOdd).reverse
}
According to your range-less proposal: the same thing but using Stream:
def isPalindrome(n:Int):Boolean = n.toString.reverse == n.toString
def ints(n: Int): Stream[Int] = Stream.cons(n, ints(n+1))
val result = ints(100).find(isPalindrome)
And with iterator (and different call method, the same thing you can do with Stream, actually):
val result = Iterator.from(100).find(isPalindrome)
But as #user unknown stated, it is direct bruteforce and not practical with large numbers.
To check if List of Any Type is palindrome using slice, without any Loops
def palindrome[T](list: List[T]): Boolean = {
if(list.length==1 || list.length==0 ){
false
}else {
val leftSlice: List[T] = list.slice(0, list.length / 2)
var rightSlice :List[T]=Nil
if (list.length % 2 != 0) {
rightSlice = list.slice(list.length / 2 + 1, list.length).reverse
} else {
rightSlice = list.slice(list.length / 2, list.length).reverse
}
leftSlice ==rightSlice
}
}
Though the simplest solution would be
def palindrome[T](list: List[T]): Boolean = {
list == list.reverse
}
You can simply use the find method on collections to find the first element matching a given predicate:
def isPalindrome(n:Int):Boolean = n.toString.reverse == n.toString
val (start, end) = (100, 1000)
val result: Option[Int] = (start to end).find(isPalindrome)
result foreach println
>Some(101)
Solution to verify if a String is a palindrome
This solution doesn't reverse the String. However I am not sure that it will be faster.
def isPalindrome(s:String):Boolean = {
s.isEmpty ||
((s.last == s.head) && isPalindrome(s.tail.dropRight(1)))
}
Solution to find next palindrome given a String
This solution is not the best for scala (pretty the same as Java solution) but it only deals with Strings and is suitable for large numbers
You just have to mirror the first half of the number you want, check if it is higher than the begin number, otherwise, increase by one the last digit of the first half and mirror it again.
First, a function to increment the string representation of an int by 1:
def incrementString(s:String):String = {
if(s.nonEmpty){
if (s.last == '9')
incrementString(s.dropRight(1))+'0'
else
s.dropRight(1) + (s.last.toInt +1).toChar
}else
"1"
}
Then, a function to compare to string representation of ints: (the function 'compare' doesn't work for that case)
/* is less that 0 if x<y, is more than 0 if x<y, is equal to 0 if x==y */
def compareInts(x:String, y:String):Int = {
if (x.length !=y.length)
(x.length).compare(y.length)
else
x.compare(y)
}
Now the function to compute the next palindrome:
def nextPalindrome(origin_ :String):String = {
/*Comment if you want to have a strictly bigger number, even if you already have a palindrome as input */
val origin = origin_
/* Uncomment if you want to have a strictly bigger number, even if you already have a palindrome as input */
//val origin = incrementString(origin_)
val length = origin.length
if(length % 2 == 0){
val (first, last) = origin.splitAt(length/2);
val reversed = first.reverse
if (compareInts(reversed,last) > -1)
first ++ reversed
else{
val firstIncr = incrementString(first)
firstIncr ++ firstIncr.reverse
}
} else {
val (first,last) = origin.splitAt((length+1)/2)
val reversed = first.dropRight(1).reverse
if (compareInts(reversed,last) != -1)
first ++ reversed
else{
val firstIncr = incrementString(first)
firstIncr ++ firstIncr.dropRight(1).reverse
}
}
}
You could try something like this, I'm using basic recursive:
object Palindromo {
def main(args: Array[String]): Unit = {
var s: String = "arara"
println(verificaPalindromo(s))
}
def verificaPalindromo(s: String): String = {
if (s.length == 0 || s.length == 1)
"true"
else
if (s.charAt(0).toLower == s.charAt(s.length - 1).toLower)
verificaPalindromo(s.substring(1, s.length - 1))
else
"false"
}
}
#tailrec
def palindrome(str: String, start: Int, end: Int): Boolean = {
if (start == end)
true
else if (str(start) != str(end))
false
else
pali(str, start + 1, end - 1)
}
println(palindrome("arora", 0, str.length - 1))