Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unsure how to perform this List Transformation in Scala optimally

Tags:

scala

Does anyone know a good way to turn the following inputList into the desired output list below?

The function I am seeking to create

def transformList(input:List[(String,String)]):List[(String,String)] = ???

input

val inputList = List(
    ("class","testClass1"),
    ("class","testClass2"),
    ("id","testId1"),
    ("class","testClassRepeat"),
    ("class","testClassRepeat"),
    ("id","testId2"),
    ("href","testHref1")
)

desired output

List(
    ("class","testClass1 testClass2 testClassRepeat testClassRepeat"),
    ("id","testId1 testId2"),
    ("href","testHref1")
)

I have a solution, but I don't think I am doing it in a good/efficient way. The solution I currently am using is:

  1. Create an empty mutable map
  2. Loop through the inputList with a .foreach
  3. Pushing key/values based on the inputList into the mutable map. Then appending to the values of existing keys if applicable (for example, there are 4 "classes" in my inputList example.)

Thanks, Phil

like image 831
Philip Nguyen Avatar asked Jun 09 '16 03:06

Philip Nguyen


3 Answers

def f(xs: List[(String, String)]): Map[String, List[String]] = 
    xs.foldRight(Map.empty[String, List[String]]){ 
          (elem: (String, String), acc: Map[String, List[String]]) =>
             val (key, value) = elem
             acc.get(key) match {
                case None     => acc + (key -> List(value))
                case Some(ys) => acc.updated(key, value :: ys)
             }
    }

scala> f(inputList)
res2: Map[String,List[String]] = Map(
       href -> List(testHref1), 
       id -> List(testId1, testId2), 
       class -> List(testClass1, testClass2, testClassRepeat, testClassRepeat)
     )
like image 200
Kevin Meredith Avatar answered Oct 23 '22 17:10

Kevin Meredith


Maybe groupBy() is what you are looking for?

scala> inputList.groupBy(_._1)
res0: Map[String,List[(String, String)]] = Map(
         href -> List((href,testHref1)),
         class -> List((class,testClass1), (class,testClass2), (class,testClassRepeat), (class,testClassRepeat)),
         id -> List((id,testId1), (id,testId2))
      )

It's also pretty straightforward to clean up the list of tuples while we are at it, eg.

scala> inputList.groupBy(_._1).map(kv => (kv._1, kv._2.map(_._2)))
res1: Map[String,List[String]] = Map(
         href -> List(testHref1),
         class -> List(testClass1, testClass2, testClassRepeat, testClassRepeat),
         id -> List(testId1, testId2)
      )
like image 5
hezamu Avatar answered Oct 23 '22 17:10

hezamu


You can use groupBy and can be done in one line.

   scala> inputList.groupBy(_._1).
          map{ case (key, value) => (key, value.map(_._2).mkString(" "))}.toList

    res0: List[(String, String)] = List(
                                       (href,testHref1), 
                                       (class,testClass1 testClass2 testClassRepeat testClassRepeat), 
                                       (id,testId1 testId2)
)
like image 4
curious Avatar answered Oct 23 '22 16:10

curious