基于键合并scala中的元组列表

时间:2019-09-25 14:08:58

标签: scala

我有一个如下所示的元组列表:

Seq("ptxt"->"how","list"->"you doing","ptxt"->"whats up","ptxt"-> "this ","list"->"is ","list"->"cool")

在键上,将 ptxt 与之后的所有列表合并。 例如 创建一个如下所示的新seq:

Seq("how you doing", "whats up", "this is cool")

4 个答案:

答案 0 :(得分:6)

您可以将SeqfoldLeft折叠:

val s = Seq("ptxt"->"how ","list"->"you doing","ptxt"->"whats up","ptxt"-> "this ","list"->"is ","list"->"cool")

val r: Seq[String] = s.foldLeft(List[String]()) {
  case (xs, ("ptxt", s)) => s :: xs
  case (x :: xs, ("list", s)) => (x + s) :: xs
}.reverse

如果您不关心订单,则可以省略reverse


函数foldLeft带有两个参数,第一个是初始值,第二个是带有两个参数的函数:先前的结果和序列的元素。然后将该方法的结果作为下一个参数传递给下一个函数调用。

例如,对于数字foldLeft,只会创建从左开始的所有元素的总和。

List(5, 4, 8, 6, 2).foldLeft(0) { (result, i) =>
  result + i
} // 25

对于我们的情况,我们从一个空列表开始。然后,我们提供函数,该函数使用模式匹配来处理两种情况。

  • 键为“ ptxt”时为大小写。在这种情况下,我们只需将值添加到列表中即可。

    case (xs, ("ptxt", s)) => s :: xs
    
  • 键为“列表”时的情况。在这里,我们从列表中获取第一个字符串(使用模式匹配),然后将值连接起来,然后将其与列表的其余部分放回去。

    case (x :: xs, ("list", s)) => (x + s) :: xs
    

最后,由于我们是前置元素,因此需要还原列表。为什么我们要添加而不是添加?因为不可变列表上的append是O(n),而prepend是O(1),所以效率更高。

答案 1 :(得分:3)

这里是另一种解决方案:

val data = Seq("ptxt"->"how","list"->"you doing","ptxt"->"whats", "list" -> "up","ptxt"-> "this ", "list"->"is cool")

第一组键和值:

val grouped = s.groupBy(_._1)
               .map{case (k, l) => k -> l.map{case (_, v) => v.trim}}

// > Map(list -> List(you doing, up, is cool), ptxt -> List(how, whats, this))

然后压缩并连接两个值:

grouped("ptxt").zip(grouped("list"))
    .map{case (a, b) => s"$a $b"}

// > List(how you doing, whats up, this is cool)

免责声明:仅当列表中始终有key, value, key, value,..时,此方法才有效-我必须调整输入数据。

答案 2 :(得分:2)

如果您将Seq更改为List,则可以使用简单的 tail-recursive 函数来解决。
(该代码使用Scala 2.13,但可以根据需要重写以使用旧的Scala版本)

def mergeByKey[K](list: List[(K, String)]): List[String] = {
  @annotation.tailrec
  def loop(remaining: List[(K, String)], acc: Map[K, StringBuilder]): List[String] =
    remaining match {
      case Nil =>
        acc.valuesIterator.map(_.result()).toList

      case (key, value) :: tail =>
        loop(
          remaining = tail,
          acc.updatedWith(key) {
            case None           => Some(new StringBuilder(value))
            case Some(oldValue) => Some(oldValue.append(value))
          }
        )
    }
  loop(remaining = list, acc = Map.empty)
}

val data = List("ptxt"->"how","list"->"you doing","ptxt"->"whats up","ptxt"-> "this ","list"->"is ","list"->"cool")
mergeByKey(data)
// res: List[String] = List("howwhats upthis ", "you doingis cool")

或者使用groupMap使用一个班轮。
(灵感来自pme的答案)

data.groupMap(_._1)(_._2).view.mapValues(_.mkString).valuesIterator.toList

答案 3 :(得分:1)

添加另一个答案,因为我没有足够的声誉点来添加评论。只是对KrzysztofAtłasik的回答的一种改进。为了补偿Seq以“列表”开头的情况,您可能需要添加另一种情况:

  case (xs,("list", s)) if xs.isEmpty=>xs

所以最终代码可能类似于:

val s = Seq("list"->"how ","list"->"you doing","ptxt"->"whats up","ptxt"-> "this ","list"->"is ","list"->"cool")

val r: Seq[String] = s.foldLeft(List[String]()) {
  case (xs,("list", s)) if xs.isEmpty=>xs
  case (xs, ("ptxt", s)) => s :: xs
  case (x :: xs, ("list", s)) => (x + s) :: xs
}.reverse