Search code examples
scaladictionaryfoldleft

SortedMap manipulation using foldleft


I have a code that converts the following datatypes:

from: SortedMap[Long, SortedMap[String, Double]] to: SortedMap[String, Array[Double]]

Can anyone please explain how this code does the above manipulation?

val convertedDataWindow = dataWindow.values.foldLeft(SortedMap.empty[String, Array[Double]]) {
  case (res0, map) =>
    (res0 /: map) {
      case (res1, (key, value)) =>
        res1.updated(key, res1.getOrElse(key, Array.empty) :+ value)
    }
}

Solution

  • Consider the inner function on its own first:

    def something(res0: SortedMap[String, Array[Double]],
      map: SortedMap[String, Double]) = (res0 /: map) {
      case (res1, (key, value)) =>
        res1.updated(key, res1.getOrElse(key, Array.empty) :+ value)
    }
    

    /: is another name for foldLeft, this is the same as

    map.foldLeft(res0) { ...}
    

    You know how foldLeft works in general? It runs through the collection, using the given function to "merge" each value into the initial value in turn. So this starts with res0, and for every (key, value) in map, we merge it into our working result using .updated: we add a new entry to the map, with key key and value res1.getOrElse(key, Array.empty) :+ value. That is, if there's already an entry for this key, we add value onto the end of it; otherwise, we make a new Array containing just value.

    So putting all that together, what this function is doing is merging map into res0, by making new entries for any new keys, or putting the values onto the Array for any existing keys.

    Now, the full function is doing another foldLeft; we could write it as:

    dataWindow.values.foldLeft(SortedMap.empty)(something)
    

    So what this is doing is starting from an empty map, and then for each of dataWindow.values in turn, merging that into our working map.

    (Of course when I talk about "merging", in reality everything is immutable and we're creating a new "working" map every time we go through the loop. But sometimes it's clearer to imagine a single, mutable "working" map res)