Kevin
Kevin

Reputation: 7946

How can I transform a list of Strings into another form in Scala?

I have a list of Strings that take the following form:

000000
011220
011220
033440
033440
000000

I'd like to transform these strings into a List of values that represents each 2x2 block while preserving row information. It is irrelevant whether the data representing each 2x2 block is stored in a list or tuple. For example, the 2x2 block representing the upper left corner could be in one of the following forms:

(0, 0, 0, 1)

List((0, 0), (0, 1))

List(0, 0, 0, 1)

List(List(0, 0), List(0, 1))

The Scala code below performs the prescribed transformation, but it seems overly complex. I can break up some of the functionality into separate functions, but I cannot see how this code can be converted into a simpler form like a for-comprehension.

NOTE: I've simplified the list of values for brevity.

scala> val data = List("000000", "011220", "033440")
data: List[String] = List(000000, 011220, 033440)

scala> val values = (data map { _.toList.sliding(2).toList } sliding(2) map { _.transpose } map { _ map { _.flatten } }).toList
values: List[List[List[Char]]] = List(List(List(0, 0, 0, 1), List(0, 0, 1, 1), List(0, 0, 1, 2), List(0, 0, 2, 2), List(0, 0, 2, 0)), List(List(0, 1, 0, 3), List(1, 1, 3, 3), List(1, 2, 3, 4), List(2, 2, 4, 4), List(2, 0, 4, 0)))

For clarity, I've relabeled some of the lists with 'Row' and 'Block', but they do not represent any concrete classes. They are simply monikers to present a more meaningful view of the output above.

values: List[Row[Block[Char]]] = List(Row(Block(0, 0, 0, 1), Block(0, 0, 1, 1), ...), Row(Block(0, 1, 0, 3), Block(1, 1, 3, 3), ...))

I am relatively new to Scala and I'd like to know if the code represented above a good way to implement this transformation or is there a more elegant way?

Upvotes: 3

Views: 104

Answers (2)

Kevin
Kevin

Reputation: 7946

This is a way to accomplish the task using a for-comprehension.

(for {
    // Create the pair of strings for each 'row'. For example: List(011220, 033440) 
    List(row1, row2) <- data.sliding(2)
  } yield (
    for {
      // Create the sequence of values representing a block.
      Seq((a1, a2),(b1, b2)) <- row1 zip row2 sliding(2)
    } yield (a1, b1, a2, b2) ).toVector
).toList

For clarity, each 2x2 block is a tuple, each row is a Vector and the entire set of rows is a List. The results in the REPL are below:

scala> val data = List("000000", "011220", "033440")
data: List[String] = List(000000, 011220, 033440)

scala> (for { List(row1, row2) <- data.sliding(2) } yield (for { Seq((a1, a2),(b1, b2)) <- row1 zip row2 sliding(2) } yield (a1, b1, a2, b2) ).toVector ).toList
res1: List[Vector[(Char, Char, Char, Char)]] = List(Vector((0,0,0,1), (0,0,1,1), (0,0,1,2), (0,0,2,2), (0,0,2,0)), Vector((0,1,0,3), (1,1,3,3), (1,2,3,4), (2,2,4,4), (2,0,4,0)))

Upvotes: 1

Marth
Marth

Reputation: 24832

You could do it this way (the .toList are essentially here to see the results instead of non-empty iterator) :

scala> val values = data.map(_.sliding(2).toList)  // create a sliding window over consecutive characters
                        .sliding(2).toList         // slide over consecutive rows
                        .flatMap { case List(row1, row2) => row1 zip row2 } // zip rows together, creating the blocks
values: List[(String, String)] = List((00,01), (00,11), (00,12), (00,22),
                                      (00,20), (01,03), (11,33), (12,34),
                                      (22,44), (20,40))

Upvotes: 3

Related Questions