Search code examples
scalatypeslist-manipulation

Scala - expand a list of various types while keeping the same types


I have a List with various types like so:

val data: List[(String, Int, List[String])] = List(("foo", 1, List("a", "b")), ("bar", 2, List("c", "d")), ("baz", 1, List("e", "f")))

Where there is an Int value large than 1, I am trying to replicate those items for each Int in that range (from 1 to Int) - so ideally it would look like (emphasizing that the Int needs to be incremented for each iteration from 1 to Int):

val dataExpanded: List[(String, Int, List[String])] = List(("foo", 1, List("a", "b")), ("bar", 1, List("c", "d")), ("bar", 2, List("c", "d")), ("baz", 1, List("e", "f")))

Working with types in scala routinely does my head in, particularly with nested strucutures since every operation seems to introduce more and more type pitfalls.

So while I am trying:

val dataExpanded = data.map{ case (s, i, l) =>

    if (i > 1) {

        List.range(1, i+1).map { n =>
            (s, n, l)
        }

    } else {
        (s, i, l)
    }
} 

I get the result:

> dataExpanded: List[Product with java.io.Serializable] = List((foo,1,List(a, b)), List((bar,1,List(c, d)), (bar,2,List(c, d))), (baz,1,List(e, f)))

The result looks like it is on the right path, but the expanded elements are nested in their own list and, as a result, I think, the Type is wrong. no amount of flatMapping or flattening or mapping the output of the for loop is getting me out of this jam.

This might also have to do with my case statement and not handling a None case, but I am not sure how to handle the other cases (I know it's not right to say, but I won't really have any other cases in my data - though that's the inebriation of dynamically typed languages talking)

Is there a way to maintain the expected types when going from data to dataExpanded while avoiding needless loops?


Solution

  • You were close, you could have done

    val dataExpanded = data.flatMap {
    case (s, i, l) =>
      List.range(1, i + 1).map { n =>
        (s, n, l)
      }
     }                                               
    
    //> dataExpanded  : List[(String, Int, List[String])] = List((foo,1,List(a, b)),
                            (bar,1,List(c, d)), (bar,2,List(c, d)), (baz,1,List(e, f)))
    

    Or more neatly as

    val dataExpanded = data.flatMap {
        case (s, i, l) =>
          (1 to i).map ((s, _, l))
      }