Scala List match last element - scala

I'm currently learning Scala and I'm amazed. The language handles so much problems quite elegant. But, I got a problem when it comes to matching the last element of a list.
Let's have a look at this code:
def stringify(list: List[String]): String = list match {
case x :: xs => x + (if (xs.length != 0) ":" else "") + stringify(xs)
case Nil => ""
}
This is quite inelegant and I would like to write it more intuitive, something like this:
def stringify(list: List[String]): String = list match {
case x :: xs => x + ":" + stringify(xs)
case x :: Nil => x
}
How can I do that?

You need to switch the order. The xs symbol will eagerly match anything in that position. Trying to match Nil first will make that statement no longer unreachable. Also, you'll still need to match Nil by itself to account for empty lists.
def stringify(list: List[String]): String = list match {
case x :: Nil => x
case x :: xs => x + ":" + stringify(xs)
case Nil => ""
}
Though mkString already does what you're looking to make.

Here's an implementation using List#foldRight:
def stringify(list: List[String]): String =
list.foldRight(""){
(e, acc) => if (acc.isEmpty) e
else { e + ":" + acc }
}
When folding over this list, we need to check for when the accumulator is empty. Otherwise, we'll get an extra : at the end of the stringfied string result.
Tests
scala> stringify(List("1", "2" , "3") )
res6: String = 1:2:3
scala> stringify(List("1", "2" , "3", "500") )
res7: String = 1:2:3:500

Related

How to get answer from this Spark Scala program for Input : s= 'aaabbbccaabb' Output : 3a3b2c2a2b

Scala program for
Input :
s= 'aaabbbccaabb'
Output :
3a3b2c2a2b
find output by spark scala ?
You can foldLeft over the input string, with a state of List[(Char, Int)]. Note that if you use Map[Char, Int], all occurrences of each character would be added up, weather they're beside each other or not.
s.foldLeft(List.empty[(Char, Int)]) {
case (Nil, newChar) => (newChar, 1) :: Nil
case (list#(headChar, headCount) :: tail, newChar) =>
if (headChar == newChar)
(headChar, headCount + 1) :: tail
else
(newChar, 1) :: list
}.map {
case (char, count) => s"$count$char"
}
.reverse // because we're prepending to the list, the reverse order of the iteration
.mkString("")
val data = "aaabbbccaabb"
val fltlist = data.toList
val ans = fltlist.map((_,1)).foldRight(Nil: List[(Char,Int)])((x,y) => y match {
case Nil => List(x)
case ((c,i)::rest) => if (c == x._1)(c, i + 1):: rest else x::y
})
var i=0
while (i< (ans.length))
{
print(ans(i)._2 + "" + ans(i)._1)
i += 1
}

Creating white space in a List in Scala

I am trying to convert a List of string to the form "rBrrBB", or "r r rb", or " rrB". The string must have length 6. If the this list is not full, then the list should be prefixed with an appropriate number of spaces
So far my code as below
def showColumn(xs: List[String]): String = xs match
{case List() => ""
case x::xs1 => x.format(" ") + showColumn(xs1)}
when I call this from
println (showColumn(List("","","b","b","r","b")))
it returns only "bbrb". It suppose to return " bbrb"
Any help would appreciate.
Try this:
def showColumn(xs: List[String]): String = xs.map(x => if(x == "") " " else x).mkString
or, alternatively:
def showColumn(xs: List[String]): String = xs.map(x => if(x.isEmpty) " " else x).mkString
Both work by changing empty strings in the list to spaces, then merging each string in the list into a single string.
If you absolutely must make this a recursive function, then a solution which is not tail-recursive would look like this:
def showColumn(xs: List[String]): String = xs match {
case Nil => ""
case x :: xs1 => (if(x.isEmpty) " " else x) + showColumn(xs1)
}
Finally, the tail-recursive version is a little more complex, as it employs a helper function:
import scala.annotation.tailrec
def showColumn(xs: List[String]): String = {
// Tail recursive helper function.
#tailrec
def nextStr(rem: List[String], acc: String): String = rem match {
case Nil => acc
case x :: xs1 => nextStr(xs1, acc + (if(x.isEmpty) " " else x))
}
// Start things off.
nextStr(xs, "")
}

Compress a Given Text of String in Scala

I have been trying to compress a String. Given a String like this:
AAABBCAADEEFF, I would need to compress it like 3A2B1C2A1D2E2F
I was able to come up with a tail recursive implementation:
#scala.annotation.tailrec
def compress(str: List[Char], current: Seq[Char], acc: Map[Int, String]): String = str match {
case Nil =>
if (current.nonEmpty)
s"${acc.values.mkString("")}${current.length}${current.head}"
else
s"${acc.values.mkString("")}"
case List(x) if current.contains(x) =>
val newMap = acc ++ Map(acc.keys.toList.last + 1 -> s"${current.length + 1}${current.head}")
compress(List.empty[Char], Seq.empty[Char], newMap)
case x :: xs if current.isEmpty =>
compress(xs, Seq(x), acc)
case x :: xs if !current.contains(x) =>
if (acc.nonEmpty) {
val newMap = acc ++ Map(acc.keys.toList.last + 1 -> s"${current.length}${current.head}")
compress(xs, Seq(x), newMap)
} else {
compress(xs, Seq(x), acc ++ Map(1 -> s"${current.length}${current.head}"))
}
case x :: xs =>
compress(xs, current :+ x, acc)
}
// Produces 2F3A2B1C2A instead of 3A2B1C2A1D2E2F
compress("AAABBCAADEEFF".toList, Seq.empty[Char], Map.empty[Int, String])
It fails however for the given case! Not sure what edge scenario I'm missing! Any help?
So what I'm actually doing is, going over the sequence of characters, collecting identical ones into a new Sequence and as long as the new character in the original String input (the first param in the compress method) is found in the current (the second parameter in the compress method), I keep collecting it.
As soon as it is not the case, I empty the current sequence, count and push the collected elements into the Map! It fails for some edge cases that I'm not able to make out!
I came up with this solution:
def compress(word: List[Char]): List[(Char, Int)] =
word.map((_, 1)).foldRight(Nil: List[(Char, Int)])((e, acc) =>
acc match {
case Nil => List(e)
case ((c, i)::rest) => if (c == e._1) (c, i + 1)::rest else e::acc
})
Basically, it's a map followed by a right fold.
Took inspiration from the #nicodp code
def encode(word: String): String =
word.foldLeft(List.empty[(Char, Int)]) { (acc, e) =>
acc match {
case Nil => (e, 1) :: Nil
case ((lastChar, lastCharCount) :: xs) if lastChar == e => (lastChar, lastCharCount + 1) :: xs
case xs => (e, 1) :: xs
}
}.reverse.map { case (a, num) => s"$num$a" }.foldLeft("")(_ ++ _)
First our intermediate result will be List[(Char, Int)]. List of tuples of chars each char will be accompanied by its count.
Now lets start going through the list one char at once using the Great! foldLeft
We will accumulate the result in the acc variable and e represents the current element.
acc is of type List[(Char, Int)] and e is of type Char
Now when we start, we are at first char of the list. Right now the acc is empty list. So, we attach first tuple to the front of the list acc
with count one.
when acc is Nil do (e, 1) :: Nil or (e, 1) :: acc note: acc is Nil
Now front of the list is the node we are interested in.
Lets go to the second element. Now acc has one element which is the first element with count one.
Now, we compare the current element with the front element of the list
if it matches, increment the count and put the (element, incrementedCount) in the front of the list in place of old tuple.
if current element does not match the last element, that means we have
new element. So, we attach new element with count 1 to the front of the list and so on.
then to convert the List[(Char, Int)] to required string representation.
Note: We are using front element of the list which is accessible in O(1) (constant time complexity) has buffer and increasing the count in case same element is found.
Scala REPL
scala> :paste
// Entering paste mode (ctrl-D to finish)
def encode(word: String): String =
word.foldLeft(List.empty[(Char, Int)]) { (acc, e) =>
acc match {
case Nil => (e, 1) :: Nil
case ((lastChar, lastCharCount) :: xs) if lastChar == e => (lastChar, lastCharCount + 1) :: xs
case xs => (e, 1) :: xs
}
}.reverse.map { case (a, num) => s"$num$a" }.foldLeft("")(_ ++ _)
// Exiting paste mode, now interpreting.
encode: (word: String)String
scala> encode("AAABBCAADEEFF")
res0: String = 3A2B1C2A1D2E2F
Bit more concise with back ticks e instead of guard in pattern matching
def encode(word: String): String =
word.foldLeft(List.empty[(Char, Int)]) { (acc, e) =>
acc match {
case Nil => (e, 1) :: Nil
case ((`e`, lastCharCount) :: xs) => (e, lastCharCount + 1) :: xs
case xs => (e, 1) :: xs
}
}.reverse.map { case (a, num) => s"$num$a" }.foldLeft("")(_ ++ _)
Here's another more simplified approach based upon this answer:
class StringCompressinator {
def compress(raw: String): String = {
val split: Array[String] = raw.split("(?<=(.))(?!\\1)", 0) // creates array of the repeated chars as strings
val converted = split.map(group => {
val char = group.charAt(0) // take first char of group string
s"${group.length}${char}" // use the length as counter and prefix the return string "AAA" becomes "3A"
})
converted.mkString("") // converted is again array, join turn it into a string
}
}
import org.scalatest.FunSuite
class StringCompressinatorTest extends FunSuite {
test("testCompress") {
val compress = (new StringCompressinator).compress(_)
val input = "AAABBCAADEEFF"
assert(compress(input) == "3A2B1C2A1D2E2F")
}
}
Similar idea with slight difference :
Case class for pattern matching the head so we don't need to use if and it also helps on printing end result by overriding toString
Using capital letter for variable name when pattern matching (either that or back ticks, I don't know which I like less :P)
case class Count(c : Char, cnt : Int){
override def toString = s"$cnt$c"
}
def compressor( counts : List[Count], C : Char ) = counts match {
case Count(C, cnt) :: tail => Count(C, cnt + 1) :: tail
case _ => Count(C, 1) :: counts
}
"AAABBCAADEEFF".foldLeft(List[Count]())(compressor).reverse.mkString
//"3A2B1C2A1D2E2F"

Runtime Exception during executing a function

I am trying to run a function in scala
def sum(xs: List[Int]): Int = xs match {
case Nil => throw new java.util.NoSuchElementException("Minimum number of elements")
case x :: xs => x + sum(xs)
}
When I try to run like this,
sum(List(1,2,3))
I am getting the runtime exception
java.util.NoSuchElementException: Minimum number of elements
at .sum(<console>:12)
at .sum(<console>:13)
On the other hand, this works
def sum(xs: List[Int]): Int = xs match {
case Nil => 0
case x :: xs => x + sum(xs)
}
List(1,2,3) is equivalent to 1::2::3::Nil
so your function is evaluated in following order
sum(1::2::3::Nil) = 1 + sum(2::3::Nil)
sum(2::3::Nil) = 2 + sum(3::Nil)
sum(3::Nil) = 3 + sum(Nil)
and at last sum(Nil) throws exception.
You can get more information from following question.
Why is Nil required at the end of a list built using the cons operator
Why do we need Nil while creating List in scala?

How to merge selected List entries together in Scala? (i.e. produce a potentially shorter list)

I have a list of text lines, and want to treat any lines ending with '\' as continuing to the next line, i.e. to merge them. The recursive code below does it, but there must be some clever way, similar to map, filter and all?
reduceLeft is close but it only produces a single result, not a modified (and potentially shorter) new list.
Also suggestions on making the code below leaner are welcome.
object TestX extends App {
// Merge lines together if the former ends with '\'.
//
private def mergeLines( list: List[String] ): List[String] = {
def merge( head: String, tail: List[String] ): List[String] = {
if (head.endsWith("\\")) {
val head2= head.dropRight(1)
if (tail.isEmpty) {
head2 :: Nil // or throw an exception on bad input
} else {
merge( head2 + tail.head, tail.tail )
}
} else {
if (tail.isEmpty)
head :: Nil
else
head :: merge( tail.head, tail.tail ) // note: cannot tailrec this
}
}
if (list.isEmpty) {
list
} else {
merge( list.head, list.tail )
}
}
val list = "These two \\" :: "should be joined" :: "but not this." :: Nil
val list2 = mergeLines(list) // any standard easy way to do this? 'list.mergeIf( _.endsWith('\\') )'
println( list2 )
assert( list2.size == 2 )
}
You can write it using foldLeft:
List("a\\", "b", "c").foldLeft(List.empty[String])((xs, x) => xs match {
case Nil => x :: Nil
case _ => if (xs.head.endsWith("\\")) (xs.head.dropRight(1) + x) :: xs.tail else x :: xs
}).reverse
It's probably not the most efficient way (fine for small list, but not for huge) as it use an immutable data structure, a more efficient approach would use a mutable List.
Here are a few tricks that you could use:
#annotation.tailrec
def mergeLines(xs: List[String], out: List[String] = Nil): List[String] = xs match {
case Nil => out.reverse
case x :: Nil => mergeLines(Nil, x :: out)
case x :: y :: rest =>
if (x endsWith """\""") mergeLines(x.init + y :: rest, out)
else mergeLines(y :: rest, x :: out)
}