Deden Bangkit - 1 year ago 61

Scala Question

After knowing some function in scala, I've tried to patch the element from list:

`val aList: List[List[Int]] = List(List(7,2,8,5,2),List(8,7,3,3,3),List(7,1,4,8,8))`

what I want to do is replacing 8 with right neighbour element if the position of 8 is head, otherwise with left neighbour if 8 is tail.

The updated aList should be:

`List[List[Int]] = List(List(7,2,2,5,2),List(7,7,3,3,3),List(7,1,4,4,4))`

I've tried the following code:

`def f(xs: List[Int]) = xs match {`

case x0 :: x1 :: x2 :: x3 :: x4 => List(x0,x1,x2,x3,x4)

case 8 :: x1 :: x2 :: x3 :: x4 => List(x1,x1,x2,x3,x4)

case x0 :: 8 :: x2 :: x3 :: x4 => List(x0,x0,x2,x3,x4)

case x0 :: x1 :: 8 :: x3 :: x4 => List(x0,x1,x1,x3,x4)

case x0 :: x1 :: x2 :: 8 :: x4 => List(x0,x1,x2,x2,x4)

case x0 :: x1 :: x2 :: x3 :: 8 => List(x0,x1,x2,x3,x3)

}

aList.flatMap(f)

The type is mismatch since the type is

`Product with java.io.Serializable`

`scala.collection.GenTraversableOnce`

Could you please explain what is the difference and how it works?

Answer Source

The problem is just in the last match pattern:

```
case x0 :: x1 :: x2 :: x3 :: 8 => List(x0,x1,x2,x3,x3)
```

You put `8`

in the position of the list tail, so it has to have the type `List[Int]`

(or more generally `GenTraversableOnce`

as the compiler tells you). If you have fixed length inner lists, you should change your patterns to have `:: Nil`

in the end:

```
case 8 :: x1 :: x2 :: x3 :: x4 :: Nil => List(x1,x1,x2,x3,x4)
...
case x0 :: x1 :: x2 :: x3 :: 8 :: Nil => List(x0,x1,x2,x3,x3)
```

An alternative is

```
case List(8, x1, x2, x3, x4) => List(x1,x1,x2,x3,x4)
...
case List(x0, x1, x2, x3, 8) => List(x0,x1,x2,x3,x3)
```

Also, your first pattern means that the other ones won't be ever reached, it just leave the list as is.

If your inner lists are not necessarily fixed-size, you need a more generic solution. Clarify, please, if that's the case.

Also, if you want to map `List[List[Int]]`

to `List[List[Int]]`

, you should use `.map(f)`

instead of `flatMap`

.

I noticed that in your example in the last sub-list you have two `8`

s replaced by the left `4`

. If you want to achieve this, you can make your function recursive and add a default case (for when all `8`

s are replaced).

```
def f(xs: List[Int]) = xs match {
case 8 :: x1 :: x2 :: x3 :: x4 :: Nil => f(List(x1,x1,x2,x3,x4))
case x0 :: 8 :: x2 :: x3 :: x4 :: Nil => f(List(x0,x0,x2,x3,x4))
case x0 :: x1 :: 8 :: x3 :: x4 :: Nil => f(List(x0,x1,x1,x3,x4))
case x0 :: x1 :: x2 :: 8 :: x4 :: Nil => f(List(x0,x1,x2,x2,x4))
case x0 :: x1 :: x2 :: x3 :: 8 :: Nil => f(List(x0,x1,x2,x3,x3))
case _ => xs
}
```

But even with these fixes this way `f`

will cycle on a list with two `8`

s in the beginning and some other edge cases. So here is a more generic solution with pattern matching:

```
def f(xs: List[Int]): List[Int] = {
// if there are only 8s, there's nothing we can do
if (xs.filter(_ != 8).isEmpty) xs
else xs match {
// 8 is the head => replace it with the right (non-8) neighbour and run recursion
case 8 :: x :: tail if x != 8 => x :: f(x :: tail)
// 8 is in the middle => replace it with the left (non-8) neighbour and run recursion
case x :: 8 :: tail if x != 8 => x :: f(x :: tail)
// here tail either starts with 8, or is empty
case 8 :: tail => f(8 :: f(tail))
case x :: tail => x :: f(tail)
case _ => xs
}
}
```