Stereo Stereo - 1 month ago 11
Scala Question

Scala matching List of tuples with guards

I new to Scala. As an exercise I am trying to write a match statement over a list of tuples with guards. I am aware that a map would solve the problem but I am trying to gain understanding into pattern matching.

I seek to write a function that takes a

List[(Char, Int)]
as argument. The function sorts the entries and if two entries have the same key value they are added together. So the following argument
List(('q', 1'), ('a', 1), ('c', 2), ('a', 2), ('c', 1))
would become
List(('a', 3), ('c', 3'), ('q', 1))
.

I come with the following code:

def sortAndAggregateList(chars: List[(Char, Int)]) : List[(Char, Int)] = {
chars match {
case (charP1, numP1) :: (charP2, numP2) :: (x : List[(String, Int)]) if (charP1 > charP2) =>
sortAndAggregateList((charP2, numP2) :: (charP1, numP1) :: x)
case (charP1, numP1) :: (charP2, numP2) :: (x : List[(String, Int)]) if (charP1 < charP2) =>
sortAndAggregateList((charP1, numP1) :: (charP2, numP2) :: x)
case (charP1, numP1) :: (charP2, numP2) :: (x : List[(String, Int)]) if (charP1 == charP2) =>
sortAndAggregateList((charP1, numP1 + numP2) :: x)
case Nil =>
Nil
}
}


But I get the following warning:

:14: warning: fruitless type test: a value of type List[(Char, Int)] cannot also be a List[(String, Int)] (the underlying of List[(String, Int)]) (but still might match its erasure)

I tried dropping the List but if I do that I get an error that
x
is of type
Any
.

Any suggestions?

Answer

the error is your type check which you do after each case statement (: List[(String, Int)]).

If you change your code to the following the error disappears:

def sortAndAggregateList(chars: List[(Char, Int)]) : List[(Char, Int)] = {
  chars match {
    case (charP1, numP1) :: (charP2, numP2) :: x if (charP1 > charP2) =>
      sortList(p1 :: p2 :: x)
    case (charP1, numP1) :: (charP2, numP2) :: x if (charP1 < charP2) =>
      sortList(p2 :: p1 :: x)
    case (charP1, numP1) :: (charP2, numP2) :: x if (charP1 == charP2) =>
        val p3: (Char, Int) = (charP1, numP1 + numP2)
        sortList(p3 :: x)
    case x =>
      x
    case Nil =>
      Nil
  }
}

Afterwards you'll find out that the compiler tells you that p1 and p2 are undefined. To fix this you need to set them as p1 = (charP1, numP1) and p2 = (charP2, numP2). To solve this with your syntax you can do the following:

def sortAndAggregateList(chars: List[(Char, Int)]) : List[(Char, Int)] = {
  chars match {
    case (charP1, numP1) :: (charP2, numP2) :: x if (charP1 > charP2) =>
      sortList((charP1, numP1) :: (charP2, numP2) :: x)
    case (charP1, numP1) :: (charP2, numP2) :: x if (charP1 < charP2) =>
      sortList((charP2, numP2) :: (charP1, numP1) :: x)
    case (charP1, numP1) :: (charP2, numP2) :: x if (charP1 == charP2) =>
        val p3: (Char, Int) = (charP1, numP1 + numP2)
        sortList(p3 :: x)
    case x =>
      x
    case Nil =>
      Nil
  }
}

Now the only missing link is the sortList function which you've not added. I'm not sure if this will work because I think the case:

case x => x

should be:

case x :: Nil => x :: Nil

Otherwise x would match anything. Which also leaves you the possibility to remove the case:

case Nil => Nil

if you don't want to remove case x => x