Building a list from tuples - scala

I'm trying to solve problem 12 taken from here
To summarize, given a list of tuples:
List((4, 'a), (1, 'b), (2, 'c), (2, 'a), (1, 'd), (4, 'e))
the result should be a list containing each element in _2 repeated _1 times
The result would be List('a, 'a, 'a, 'a, 'b, 'c, 'c, 'a, 'a, 'd, 'e, 'e, 'e, 'e)
This is my attempt (see code online)
object P12 extends App {
val ls2 = List((4, 'a), (1, 'b), (2, 'c), (2, 'a), (1, 'd), (4, 'e))
println(ls2)
println(decode(ls2))
def decode[A] (list: List[(Int,A)]): List[A] = {
list.foldLeft(List[A]()) { (result, elem) => for ( n <- 1 to elem._1) result :+ elem._2 }
}
}
I get the following error:
Main.scala:9: error: type mismatch;
found : Unit
required: List[A]
list.foldLeft(List[A]()) { (result, elem) => for ( n <- 1 to elem._1) result :+ elem._2 }
how to fix this?

Use List.fill and a flatMap:
val l = List((4, 'a), (1, 'b), (2, 'c), (2, 'a), (1, 'd), (4, 'e))
l.flatMap { t => List.fill(t._1)(t._2) }

Your for cycle does nothing, are you are using it in imperative way with immutable collection. On each iteration you create new collection from result with appended element and then just discard it. The result of your for is Unit, hence the error.
The correct functional way to perform your task is this:
def decode[A] (list: List[(Int,A)]): List[A] = {
list.foldLeft(List[A]()) { case (result, (count, el)) =>
result ::: (1 to count).map(_ => el).toList
}
}
UPD: Also please note, that append operation on List takes linear time, so the whole decode becomes O(n2).
There is more efficient and concise way to perform your task, using flatMap:
def decode[A](list: List[(Int, A)]): List[A] =
list.flatMap { case (count, el) => (1 to count).map(_ => el) }

This recursive function denotes (or lists) the semantics concisely in two cases,
def decode( xs: List[(Int,Symbol)] ): List[Symbol] = xs match {
case Nil => Nil
case (x :: xss) => List.fill(x._1)(x._2) ++ decode(xss)
}
An empty list results in an empty list, else for the head element of a non empty list, expand it into a list of repeated elements and prepend it to the rest of the decoded list.

Related

self defined combination function in Scala

I want to implement a combination function in scala and my code is as below:
def combination[A](n: Int, ls: List[A]): List[List[A]] ={
(n, ls) match{
case (_, Nil) => List(Nil)
case (1, _) => ls.map(List(_))
case (_, _) if n > ls.size => List.empty
case _ => ls.flatMap(x => combination(n - 1, subList(x, ls)).map(x :: _))
}
}
def subList[A](e: A, in: List[A]) = in.takeRight(in.size - in.indexOf(e) - 1)
but the result is not what I expect. When I call combination(3, List('a, 'b, 'c, 'd, 'e, 'f). it will give me those results with one or two elements like List('d, 'f), List('f) and so on. Can anyone help me to find the issue? Thanks.
Update:
the correct version is
def combination[A](n: Int, ls: List[A]): List[List[A]] ={
(n, ls) match{
case (_, Nil) => Nil
case (1, _) => ls.map(List(_))
case (_, _) if n > ls.size => Nil
case _ => ls.flatMap(x => combination(n - 1, subList(x, ls)).map(x :: _))
}
}
def subList[A](e: A, in: List[A]) = in.takeRight(in.size - in.indexOf(e) - 1)
Too many cases, unnecessary complicated combinations2 and subList.
Recall how combinations are defined. If you have a set {a1, ..., aN}, and you want to select k elements from this set, then you can do essentially just two things:
You don't include a1 into the subset. Then you have to select k elements from the remaining {a2, ..., aN}.
You include a1 into the subset. Then you have to select k-1 elements from {a2, ..., aN}, and add a0 to those k-1 elements.
That's where the formula
C(N, k) = C(N - 1, k) + C(N - 1, k - 1)
comes from.
Translated into code, this is just:
def comb[A](ls: List[A], k: Int): List[List[A]] = {
if (k == 0) List(Nil)
else ls match {
case Nil => List()
case h :: t => comb(t, k) ++ comb(t, k - 1).map(h :: _)
}
}
Example:
comb(List('a, 'b, 'c, 'd, 'e), 3) foreach println
gives:
List('c, 'd, 'e)
List('b, 'd, 'e)
List('b, 'c, 'e)
List('b, 'c, 'd)
List('a, 'd, 'e)
List('a, 'c, 'e)
List('a, 'c, 'd)
List('a, 'b, 'e)
List('a, 'b, 'd)
List('a, 'b, 'c)
In the last case you are calling combination2. If you change to combination (with reasonable definition of subList) it works. I guess this is something remaining from an intermediate version.
There's a much simpler way of doing this
def comb[A](n: Int, ls:List[A]): List[List[A]] = {
if(ls.isEmpty) List(Nil)
else ls.toSet[A].subsets.map(_.toList).filter(_.length == n).toList
}
Scala fiddle

Scala: How to merge lists by the first element of the tuple

Let say I have a list:
[(A, a), (A, b), (A, c), (B, a), (B, d)]
How do I make that list into:
[(A, [a,b,c]), (B, [a,d])]
with a single function?
Thanks
The groupBy function allows you to achieve this:
scala> val list = List((1, 'a'), (1, 'b'), (1, 'c'), (2, 'a'), (2, 'd'))
list: List[(Int, Char)] = List((1,a), (1,b), (1,c), (2,a), (2,d))
scala> list.groupBy(_._1) // grouping by the first item in the tuple
res0: scala.collection.immutable.Map[Int,List[(Int, Char)]] = Map(2 -> List((2,a), (2,d)), 1 -> List((1,a), (1,b), (1,c)))
Just doing groupBy won't give you the expected format you desire. So i suggest you write a custom method for this.
def groupTuples[A,B](seq: Seq[(A,B)]): List[(A, List[B])] = {
seq.
groupBy(_._1).
mapValues(_.map(_._2).toList).toList
}
Then then invoke it to get the desired result.
val t = Seq((1,"I"),(1,"AM"),(1, "Koby"),(2,"UP"),(2,"UP"),(2,"AND"),(2,"AWAY"))
groupTuples[Int, String](t)

Convert list counting repeated elements

I'm trying to solve problem 13 from here using recursion, but I get an error (which I don't understand).
The problem is:
Given the following list List('a, 'a, 'a, 'a, 'b, 'c, 'c, 'a, 'a, 'd, 'e, 'e, 'e, 'e)
I need to return repeated elements and count:
List((4,'a), (1,'b), (2,'c), (2,'a), (1,'d), (4,'e))
This is my code:
object P13 extends App {
val ls2 = List('a, 'a, 'a, 'a, 'b, 'c, 'c, 'a, 'a, 'd, 'e, 'e, 'e, 'e)
println(ls2)
println(encodeDirect(ls2))
def encodeDirect[A](ls: List[A]): List[(Int,A)] = ls match {
case h :: tail => (ls.takeWhile( _ == h ).count(_), h) +: encodeDirect (ls.dropWhile ( _ == h ) )
case Nil => Nil
}
}
This is the error:
P13.scala:18: error: type mismatch;
found : List[(Any, A)]
required: List[(Int, A)]
case h :: tail => (ls.takeWhile( _ == h ).count(_), h) +: encodeDirect
(ls.dropWhile ( _ == h ) )
^
one error found
Why is this happening and how to fix this?
Your mistake is quite simple, you used count instead of size/length:
def encodeDirect[A](ls: List[A]): List[(Int,A)] = ls match {
case h :: tail => (ls.takeWhile( _ == h ).size, h) +: encodeDirect (ls.dropWhile ( _ == h ) )
case Nil => Nil
}
count takes predicate and calculates the number of elements that match that predicate, while size just returns the length of the collection.
Just for fun, here is an alternative, that utilizes span, that breaks collection into prefix/suffix:
def encodeDirect[A](ls: List[A]): List[(Int,A)] =
ls.headOption.map(h => ls.span(h == _)).toList
.flatMap { case (pref, t) => (pref.size, pref.head) :: encodeDirect(t) }
Also it may be a good idea to rewrite this function in tail-recursive way, using results accumulator as an argument, as tail recursion is more efficient in scala.
Another approach, using span,
def encode(xs: List[Symbol]): List[(Int,Symbol)] = xs match {
case Nil => Nil
case x :: xss => val (g,rest) = xs.span(_ == x)
(g.size,g.head) +: encode(rest)
}
where we bisect the list into identical items to the head first, and then the rest.

Convert list of tuples with duplicates to tuple with nested tuple

I am looking for a nice way to convert a list of tuples such as
List((a,b,c,d,e,f),(a,b,c,g,h,i))
to something like
(a,b,c,List((d,e,f),(g,h,i)))
The list can be very large, up to 1000 tuples and the tuples have 20 fields with data types string and integers.
So far, I have figured out the following :
val l = List(("a","b","c","d","e","f"),("a","b","c","g","h","i"))
val l2= l.groupBy(x =>(x._1, x._2, x._3))
//Map((a,b,c) -> List((a,b,c,d,e,f), (a,b,c,g,h,i)))
val l3 = l2.map{case (k,v) =>(k._1, k._2, v.map{y =>(y._4,y._5)})}
//List((a,b,List((c,d,e), (c,g,h))))
But it is ugly and looks like won't work well for the large list
Doing this with tuples is not trivial. I suggest looking at HList implementations.
Assuming all items are of the same type you can define your input as:
val input: List[Set[Symbol]] = List(
Set('a, 'b, 'c, 'd, 'e, 'f),
Set('a, 'b, 'c, 'g, 'h, 'i)
)
And then compute your result using Set's intersection & difference:
val output: (Set[Symbol], List[Set[Symbol]]) = {
val intersection = input.reduce(_ & _)
val differences = input.map(_ &~ intersection)
(intersection, differences)
}
The result would be:
(Set('a, 'b, 'c), List(Set('e, 'f, 'd), Set('i, 'g, 'h)))
Using shapeless, its one line solution
import shapeless.syntax.std.tuple._
val l = List(("a", "b", "c", "d", "e", "f"), ("a", "b", "c", "g", "h", "i"))
l(0).take(3) :+ l.map(_.drop(3))
//res0: (String, String, String, List[(String, String, String)]) =
(a,b,c,List((d,e,f), (g,h,i)))

Scala - Pattern Matching and For loop issue

I'm trying to solve the problem 12 of S-99: Ninety-Nine Scala Problems
Given a run-length code list generated as specified in problem P10,
construct its uncompressed version. Example:
scala> decode(List((4, 'a), (1, 'b), (2, 'c), (2, 'a), (1, 'd), (4, 'e)))
res0: List[Symbol] = List('a, 'a, 'a, 'a, 'b, 'c, 'c, 'a, 'a, 'd, 'e, 'e, 'e, 'e)
I was trying to pattern match the element in the list and then use a for loop to concatenate the char but I've got the following compilation error on line 5 :
type mismatch; found : scala.collection.immutable.IndexedSeq[List[A]] required: List[A]
1 def decode[A](xs: List[(Int, A)]) : List[A] = xs match {
2 case Nil => Nil
3 case x :: xs => {
4 for {
5 i <- 1 to x._1
6 } yield (x._2) :: decode(xs)
7 }
8 }
Sorry but I begin Scala. Could someone explain why this is happening and how to solve it ?
You are quite close - just a couple of problems. Here is a fixed version I came up with:
def decode[A](xs: List[(Int, A)]) : List[A] = xs match {
case Nil => Nil
case x :: xs => (for {
i <- 1 to x._1
} yield (x._2)).toList ::: decode(xs)
}
The first - and probably most important - thing is the extra parentheses around the for-yield. Without this, you are trying to yield (x._2) :: decode(xs), rather than just (x._2) (to make up for that, the {} around the whole case can be omitted).
Next, the for-yield results in an IndexedSeq rather than a List, so I forced a conversion to List (you could handle this in various ways, this was merely the most expedient).
Finally, concatenating to the List resulting from decode(xs) requires the ::: operator (you can also use ++) rather than :: (which prepends a single entry, not a sub-list).
The main issue is the operator you use for concatenating lists - :: is used only to prepend a single element to a list, so in your code you are trying to prepend the result of the yield (which is itself a sequence) to a List[A] and get type incompatibility as a result. Here is a modified version that will work - it uses operator ++: which can be used to join two sequences together. I also moved the yield to a separate statement, otherwise you would need parentheses around the yield so that ++: works on the complete result of the yield and not on each element (which would again not compile due to types not matching).
def decode[A](xs: List[(Int, A)]) : List[A] = xs match {
case Nil => Nil
case x :: xs => {
val repeatedElems = for {
i <- 1 to x._1
} yield (x._2)
repeatedElems ++: decode(xs)
}
}
The other answers are perfectly fine but I think generating the decoded lists with List.fill requires less syntax and is easier to understand compared to the for-yield expression.
def decode[A](xs: List[(Int, A)]) : List[A] = xs match {
case Nil => Nil
case x :: xs => List.fill(x._1)(x._2) ::: decode(xs)
}
Output:
scala> decode(List((4, 'a), (1, 'b), (2, 'c), (2, 'a), (1, 'd), (4, 'e)))
res0: List[Symbol] = List('a, 'a, 'a, 'a, 'b, 'c, 'c, 'a, 'a, 'd, 'e, 'e, 'e, 'e)
Here is a slightly modified version of Brian's answer. Decomposing the tuple makes the code even more readable:
def decode[A](xs: List[(Int, A)]) : List[A] = xs match {
case Nil => Nil
case (count, letter) :: xs => List.fill(count)(letter) ::: decode(xs)
}
Another method is using map:
def decode[A](l: List[(Int, A)]): List[A] = {
val l1: List[List[A]] = l map { e =>
List.fill(e._1)(e._2)
}
l1.flatten
}