如何跨多个元素(例如,超前)进行映射?

时间:2019-06-21 05:04:08

标签: scala

我有一个List [Int],也许像这样:

List(1,2,3,3,4,5,6,6,7,8,9)

偶尔会有重复(总是2次,而不是更多)。有dup时,我想将元素与函数合并在一起。因此,在这个简单的示例中,如果我的功能是将两个数字相加,我将得到:

List(1,2,6,4,5,12,7,8,9)

执行此操作的简洁方法是什么? List.map()一次只能查看/转换1个元素。

2 个答案:

答案 0 :(得分:2)

您可以将.foldLeft与新列表一起用作累加器假设列表彼此相邻重复

def mergeDuplicates(list: List[Int]): List[Int] = {
  list.foldLeft(List.empty[Int]) {
    case (l, elem) if l.lastOption.contains(elem) =>
      l.dropRight(1) :+ (2 * elem)
    case (l, elem) =>
      l :+ elem
  }
}

println(mergeDuplicates(List(1, 2, 3, 3, 4, 5, 6, 6, 7, 8, 9))) 

// output List(1, 2, 6, 4, 5, 12, 7, 8, 9)

使用extension method

implicit class ListOps(list: List[Int]) {
  def mergeDuplicates: List[Int] = {
    list.foldLeft(List.empty[Int]) {
      case (l, elem) if l.lastOption.contains(elem) =>
        l.dropRight(1) :+ (2 * elem)
      case (l, elem) =>
        l :+ elem
    }
  }
}

val mergedList = List(1, 2, 3, 3, 4, 5, 6, 6, 7, 8, 9).mergeDuplicates
println(mergedList)

答案 1 :(得分:2)

Scala 2.13开始,您可以将List#unfoldList#span结合使用:

// val items = List(1, 2, 3, 3, 4, 5, 6, 6, 7, 8, 9)
List.unfold(items) {
  case Nil  => None
  case rest => Some(rest.span(_ == rest.head))
}
.map(_.sum)
// List(1, 2, 6, 4, 5, 12, 7, 8, 9)

,甚至结合Scala 2.13的{​​{3}}构建器:

List
  .unfold(items)(rest => Option.unless(rest.isEmpty)(rest.span(_ == rest.head)))
  .map(_.sum)

  • “展开”使用的内部状态在我们的例子中是用列表List(1, 2, 3, 3, 4, 5, 6, 6, 7, 8, 9)初始化的,该列表要对其连续的重复项求和。
  • 在每次迭代过程中,我们span使用该内部状态来查找标题的连续项:rest.span(_ == rest.head)(例如,在List(3, 3, 4, 5, ...)内部状态的第三次迭代期间,{{{ 1}})。
  • 由于(List(3, 3), List(5, 6, ...))在每个迭代中期望一个元组的unfold,其第一部分是要添加到要构建的列表中的新元素(例如,在第一次迭代中,Option),其第二部分是内部状态的新值(List(1)),该范围完全符合该要求。
  • 我们一直重复相同的步骤,直到内部列表为空,在这种情况下,返回List(2, 3, 3, ...)表示None已经完成,并且迭代必须停止。
  • 最后,我们用一个简单的unfold用它们的总和替换分组的部分(List(List(1), ..., List(3, 3), ...))。当然,这也可以在展开的过程中实现,但要付出一些额外的样板。