I have the following code snippet that I would like to optimize:
import org.joda.time.{DateTime, Days, Months, Weeks, Years}
#scala.annotation.tailrec
def optimize(start: DateTime, end: DateTime, acc: List[(DateTime, DateTime)]): List[(DateTime, DateTime)] = {
def doDaysExist(startDate: DateTime, endDate: DateTime): Boolean = {
Days.daysBetween(startDate, endDate).getDays != 0
}
if (Years.yearsBetween(start, end.plusDays(1)).getYears != 0) {
val years = Years.yearsBetween(start, end.plusDays(1)).getYears
val newStart = start.plusYears(years).minusDays(1)
if (doDaysExist(newStart, end)) {
optimize(newStart, end, (start, newStart) +: acc)
} else (start, end) +: acc
} else if (Months.monthsBetween(start, end.plusDays(1)).getMonths != 0) {
val months = Months.monthsBetween(start, end.plusDays(1)).getMonths
val newStart = start.plusMonths(months).minusDays(1)
if (doDaysExist(newStart, end)) {
optimize(newStart, end, (start, newStart) +: acc)
} else (start, end) +: acc
} else if (Weeks.weeksBetween(start, end.plusDays(1)).getWeeks != 0) {
val weeks = Weeks.weeksBetween(start, end.plusDays(1)).getWeeks
val newStart = start.plusWeeks(weeks).minusDays(1)
if (doDaysExist(newStart, end)) {
optimize(newStart, end, (start, newStart) +: acc)
} else (start, end) +: acc
} else {
// For sure days
(start, end) +: acc
}
}
As it can be seen that the if condition and the next statement that follows are kind of duplicate code. Is there a better way to make it better and cryptic? I have to step through this hierarchy as I have to first check the year, then the month, then the week and then the day.
I can think of extracting the repeated code block into lambdas, but is that good enough? I can't think of rewriting it as a Map as that code block runs in a tail recursive function.
What about something like this:
def optimize(start: DateTime, end: DateTime, acc: List[(DateTime, DateTime)]): List[(DateTime, DateTime)] = {
val endPlusDay = end.plusDays(1)
val checks = List(
{
val years = Years.yearsBetween(start, endPlusDay).getYears
val newStart = start.plusYears(years).minusDays(1)
(years -> newStart)
},
{
val months = Months.monthsBetween(start, endPlusDay).getMonths
val newStart = start.plusMonths(months).minusDays(1)
(months -> newStart)
},
{
val weeks = Weeks.weeksBetween(start, endPlusDay).getWeeks
val newStart = start.plusWeeks(weeks).minusDays(1)
(weeks -> newStart)
}
)
checks.collectFirst {
case (difference, newStart) if (
(difference != 0) && (Days.daysBetween(newStart, end) != 0)
) =>
optimize(newStart, end, acc :+ (start, newStart))
}.getOrElse(acc :+ (start, end))
}
PS: Two recommendations
Use java.time instead of Joda.
Prepend and finally reverse to the accumulator instead of append.
Just pre-compute the values and move the second test outside the if/else:
val years = Years.yearsBetween(start, end.plusDays(1)).getYears
lazy val months = Months.monthsBetween(start, end.plusDays(1)).getMonths
lazy val weeks = Weeks.weeksBetween(start, end.plusDays(1)).getWeeks
val newStart =
if (years != 0) {
start.plusYears(years).minusDays(1)
} else if (months != 0) {
start.plusMonths(months).minusDays(1)
} else if (weeks != 0) {
start.plusWeeks(weeks).minusDays(1)
} else {
start
}
if (doDaysExist(newStart, end)) {
optimize(newStart, end, (start, newStart) +: acc)
} else {
(start, end) +: acc
}
Using lazy may or may not improve performance so measure to see which is best.
Related
val date2 = Option(LocalDate.parse("2017-02-01"))
//date1.compareTo(date2)>=0
case class dummy(val prop:Seq[Test])
case class Test(val s :String)
case class Result(val s :String)
val s = "11,22,33"
val t = Test(s)
val dt =Test("2017-02-06")
val list = dummy(Seq(t))
val list2 = dummy(Seq(dt))
val code = Option("22")
val f = date2.flatMap(c => list2
.prop
.find(d=>LocalDate.parse(d.s)
.compareTo(c)>=0))
.map(_ => Result("Found"))
.getOrElse(Result("Not Found"))
code.flatMap(c => list
.prop
.find(_.s.split(",").contains(c)))
.map(_ => Result("Found"))
.getOrElse(Result("Not Found"))
I want to && the conditions below and return Result("Found")/Result("Not Found")
d=>LocalDate.parse(d.s).compareTo(c)>=0)
_.s.split(",").contains(c)
Is there any possible way to achieve the above .In actual scenerio list and list 2 are Future
I tried to make a more realistic example based on Futures. Here is how I would do it:
val date2 = Option(LocalDate.parse("2017-02-01"))
case class Test(s: String)
case class Result(s: String)
val t = Test("11,22,33")
val dt = Test("2017-02-06")
val code = Option("22")
val f1 = Future(Seq(t))
val f2 = Future(Seq(dt))
// Wait for both futures to finish
val futureResult = Future.sequence(Seq(f1, f2)).map {
case Seq(s1, s2) =>
// Check the first part, this will be a Boolean
val firstPart = code.nonEmpty && s1.exists(_.s.split(",").contains(code.get))
// Check the second part, also a boolean
val secondPart = date2.nonEmpty && s2.exists(d => LocalDate.parse(d.s).compareTo(date2.get) >= 0)
// Do the AND logic you wanted
if (firstPart && secondPart) {
Result("Found")
} else {
Result("Not Found")
}
}
// This is just for testing to see we got the correct result
val result = Await.result(futureResult, Duration.Inf)
println(result)
As an aside, your code and date2 values in your example are Options... If this is true in your production code, then we should do a check first to see if they are both defined. If they are not then there would be no need to continue with the rest of the code:
val futureResult = if (date2.isEmpty || code.isEmpty) {
Future.successful(Result("Not Found"))
} else {
Future.sequence(Seq(f1, f2)).map {
case Seq(s1, s2) =>
val firstPart = s1.exists(_.s.split(",").contains(code.get))
val secondPart = s2.exists(d => LocalDate.parse(d.s).compareTo(date2.get) >= 0)
if (firstPart && secondPart) {
Result("Found")
} else {
Result("Not Found")
}
}
}
Use pattern matching on Option instead of using flatMap
e.g.
val x = Some("20")
x match {
case Some(i) => println(i) //do whatever you want to do with the value. And then return result
case None => Result("Not Found")
}
Looking at what you are trying to do, You would have to use pattern matching twice, that too nested one.
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)}")
}
}
I am having following code for getting start dates for date range period provided
def dayIterator(start: DateTime, end: DateTime, period: String) = {
period match {
case "day" => Iterator.iterate(start)(_ plusDays 1) takeWhile (_ isBefore end)
case "week" => Iterator.iterate(start)(_ plusWeeks 1) takeWhile (_ isBefore end)
case "month" => Iterator.iterate(start)(_ plusMonths 1) takeWhile (_ isBefore end)
}
}
following code extracts the date based on period
scala> dayIterator(new DateTime("2017-09-13"), new DateTime("2017-09-27"), "week").foreach(println)
2017-09-13T00:00:00.000+05:30
2017-09-20T00:00:00.000+05:30
I want start dates and end dates of week(or month) by offsetting first day of week as monday (or months' first day as 1st or start_date given)
so I expect output is like following (day name is mentioned for ref)
2017-09-13T00:00:00.000+05:30 (Wednesday) -- 2017-09-17T00:00:00.000+05:30 (Sunday)
2017-09-18T00:00:00.000+05:30 (Monday) -- 2017-09-24T00:00:00.000+05:30 (Sunday)
2017-09-25T00:00:00.000+05:30 (Monday) -- 2017-09-27T00:00:00.000+05:30 (Wednesday)
I have done some trials and found following solution, I am converting final result dates to java.util.Date and putting it in a SortedMap
Following is the dayGroupGenerator function which takes care of offsetting
def dayGroupGenerator(startx: DateTime, endx: DateTime, period: String): scala.collection.mutable.ListBuffer[(Date, Date)] = {
var start = startx
var end = endx
var dateSlots = scala.collection.mutable.ListBuffer[(Date, Date)]()
if (start.isAfter(end)) {
dateSlots
}
else if (start.equals(end)) {
dateSlots += ((start.toDate, end.toDate))
}
else {
period match {
case "day" => {
while (start.equals(end) || start.isBefore(end)) {
dateSlots += ((start.toDate, (if (start.equals(end) || start.isAfter(end)) end else start).toDate))
start = start.plusDays(1)
}
}
case "week" => {
if (start.dayOfWeek.get != 1) {
dateSlots += ((start.toDate, (if (start.withDayOfWeek(7).isAfter(end)) end else start.withDayOfWeek(7)).toDate))
start = start.withDayOfWeek(7).plusDays(1)
}
while (start.equals(end) || start.isBefore(end)) {
dateSlots += ((start.toDate, (if (start.equals(end) || start.withDayOfWeek(7).isAfter(end)) end else start.withDayOfWeek(7)).toDate))
start = start.withDayOfWeek(7).plusDays(1)
}
}
case "month" => {
if (start.dayOfMonth.get != 1) {
dateSlots += ((start.toDate, (if (start.dayOfMonth.withMaximumValue.isAfter(end)) end else start.dayOfMonth.withMaximumValue).toDate))
start = start.dayOfMonth.withMaximumValue.plusDays(1)
}
while (start.equals(end) || start.isBefore(end)) {
dateSlots += ((start.toDate, (if (start.equals(end) || start.dayOfMonth.withMaximumValue.isAfter(end)) end else start.dayOfMonth.withMaximumValue).toDate))
start = start.dayOfMonth.withMaximumValue.plusDays(1)
}
}
}
}
dateSlots
}
You can paste above code in scala console as it is.
Call above function by using following scala console examples to see what I have tried to achieve
scala> import scala.collection.SortedMap
scala> import java.util.Date
scala> import org.joda.time.Days
scala> import org.joda.time.DateTime
scala> dayGroupGenerator(new DateTime("2017-01-07"), new DateTime("2017-01-07"), "day").foreach(println)
scala> dayGroupGenerator(new DateTime("2017-01-31"), new DateTime("2017-02-01"), "week").foreach(println)
scala> dayGroupGenerator(new DateTime("2017-01-11"), new DateTime("2017-03-01"), "month").foreach(println)
scala> dayGroupGenerator(new DateTime("2017-02-01"), new DateTime("2017-02-03"), "day").foreach(println)
scala> dayGroupGenerator(new DateTime("2017-02-11"), new DateTime("2017-02-27"), "week").foreach(println)
scala> dayGroupGenerator(new DateTime("2017-03-13"), new DateTime("2017-05-30"), "month").foreach(println)
Note: I have not used DateTime in return type (Map[]) and used simpler java.util.Date because console was giving me following error for implicit Ordering of Joda DateTime
error: No implicit Ordering defined for org.joda.time.DateTime.
var dateSlots = scala.collection.SortedMap[DateTime, DateTime]()
Assuming that I have a collection (Vector[Int]),1,2,5,4,3,5,5,5,6,7,7 and want to get another collection (Vector[Vector[Int]]) pairing every number 5 with the next number (1),(2),(5,4),(3),(5,5),(5,6),(7),(7) what are my options apart from this:
var input= Vector.[Int]
var output = Vector.empty[Vector[Int]]
var skip = false
for(i <- input.indices){
if (input(i) == 5 && skip == false){
output = output :+ input(i) :+ input(i + 1)
skip = true;
}else if(input(i - 1) != 5){
output = output :+ input(i)
}else{
skip = false;
}
}
which works but is not very scala-like.
Would it be possible to achieve the same result with a for comprehension? for(x <- c; if cond) yield {...}
You can use foldLeft
val output = input.foldLeft (Vector.empty[Vector[Int]]) { (result, next) =>
if(!result.isEmpty && result.last == Vector(5)) {
result.dropRight(1) :+ Vector(5, next)
} else {
result :+ Vector(next)
}
}
You could use pattern matching as well
def prepareVector(lv: Vector[Int]): Vector[Vector[Int]] = {
val mv = new ArrayBuffer[Vector[Int]]()
def go(ll: List[Int]): Unit = ll match {
case y :: Nil => mv += Vector(y)
case 5 :: ys => {
mv += Vector(5, ys.head)
go(ys.tail)
}
case y :: ys => {
mv += Vector(y)
go(ys)
}
case Nil => None
}
go(lv.toList)
mv.toVector
}
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))