不确定如何以最佳方式在Scala中执行此列表转换

时间:2016-06-09 03:37:40

标签: scala

有没有人知道将以下inputList转换为下面所需的输出列表的好方法?

我想要创造的功能

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

输入

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

期望的输出

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

我有一个解决方案,但我不认为我是以一种好/有效的方式做到这一点。我目前使用的解决方案是:

  1. 创建一个空的可变地图
  2. 使用.foreach
  3. 循环使用inputList
  4. 将基于inputList的键/值推送到可变映射中。然后附加到现有键的值(如果适用)(例如,我的inputList示例中有4个“类”。)
  5. 谢谢, 菲尔

6 个答案:

答案 0 :(得分:9)

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)
     )

答案 1 :(得分:5)

也许groupBy()正是您要找的?

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))
      )

在我们处理元组列表的同时清理元组列表也非常简单,例如。

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)
      )

答案 2 :(得分:4)

您可以使用groupBy,可以在一行中完成。

   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)
)

答案 3 :(得分:2)

您可以对已排序的集合使用foldLeft:

def transformList(input:List[(String,String)]):List[(String,String)] =
  input
    .sortBy(_._1)
    .foldLeft(List[(String, String)]()) {
      case ((xn,xv)::xs, (name, value)) if xn==name => (xn, xv + " " + value)::xs
      case (xs, item) => item::xs
    }

答案 4 :(得分:2)

另一种理解但同时使用groupBy的方法(参见@hezamu):

val m = 
  for { (k, xs) <- inputList.groupBy(_._1)
         s = xs.map(_._2).mkString(" ")
      }
  yield k -> s

然后

m.toMap

答案 5 :(得分:2)

您可以将groupByfor comprehension一起使用,使代码更符合关系SQL概念,其中您需要的结果类似于键上的Group By,然后转换分组结果,在这种情况下连接字符串:

def transformList(input:List[(String,String)]):List[(String,String)] = {
  (for {
    // Return the generator for key-values of grouped result
    (k, v) <- input.groupBy(y => y._1)
    // For every list in the grouped result return the concatenated string
    z = v.map(_._2).mkString(" ")
  } yield k -> z)
  .toList

}