Reputation: 7946
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
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
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