我有一个如下所示的元组列表:
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")
答案 0 :(得分:6)
您可以将Seq
与foldLeft
折叠:
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