如何从Scala列表中删除每个第n个元素?

时间:2016-08-23 15:23:25

标签: scala scala-collections

是否有任何方法可以从Scala n th element删除每个List

我希望我们可以在filter方法中执行此操作,并通过编写逻辑返回另一个列表。但这是一种有效的方法吗?

8 个答案:

答案 0 :(得分:7)

Simplest so far, I think

def removeNth[A](myList: List[A], n: Int): List[A] = 
  myList.zipWithIndex collect { case (x,i) if (i + 1) % n != 0 => x }

collect is an oft-forgotten gem that takes a partial function as its second argument, maps elements with that function and ignores those that are not in its domain.

答案 1 :(得分:2)

Simply:

list.zipWithIndex
    .filter { case (_, i) => (i + 1) % n != 0 }
    .map { case (e, _) => e }

答案 2 :(得分:1)

一种没有索引的方法,将列表分成长度为nth的块,

xs.grouped(nth).flatMap(_.take(nth-1)).toList

grouped发送的每个块中,我们最多可以使用nth-1个项目。

这另一种方法效率不高(请注意@ Alexey Romanov的评论),使用for desrehears进入flatMapwithFilter lazy 过滤器) ,

for (i <- 0 until xs.size if i % nth != nth-1) yield xs(i)

答案 3 :(得分:1)

这是一个没有索引的递归实现。

  def drop[A](n: Int, lst: List[A]): List[A] = {
    def dropN(i: Int, lst: List[A]): List[A] = (i, lst) match {
      case (0, _ :: xs) => dropN(n, xs)
      case (_, x :: xs) => x :: dropN(i - 1, xs)
      case (_, x) => x
    }
    dropN(n, lst)
  }

答案 4 :(得分:1)

另一个替代方案,接近@ elm的回答,但考虑到drop(1)列表要比take几乎整个列表快得多:

def remove[A](xs: List[A], n: Int) = {
  val (firstPart, rest) = xs.splitAt(n - 1)
  firstPart ++ rest.grouped(n).flatMap(_.drop(1))
}

答案 5 :(得分:1)

以下是使用累加器的List的尾递归实现:

  import scala.annotation.tailrec
  def dropNth[A](lst: List[A], n: Int): List[A] = {
    @tailrec
    def dropRec(i: Int, lst: List[A], acc: List[A]): List[A] = (i, lst) match {
      case (_, Nil) => acc
      case (1, x :: xs) => dropRec(n, xs, acc)
      case (i, x :: xs) => dropRec(i - 1, xs, x :: acc)
    }
    dropRec(n, lst, Nil).reverse
  }

更新:正如评论中所述,我在大型(1 to 5000000).toList输入中尝试了其他解决方案。 zipWithIndex filter/collect OutOfMemoryError失败的人StackOverflowError失败而{尾部}失败导致::失败。我使用List cons(tailrec)和ListBuffer效果很好。

这是因为zipping-with-index会创建新的OOM并附加元组,从而导致::。 递归只有500万级递归,这对于堆栈来说太多了。

tail-recursive不会创建任何不必要的对象,并且在O(n)中有效地创建了两个输入副本(即,2 * 5百万个x :: acc个实例)。第一种是创建过滤后的元素,这些元素的顺序相反,因为输出前置O(1)(在List中,而附加O(n)undo add)。第二个就是递归输出的反转。

答案 6 :(得分:1)

最简单的解决方案

scala> def dropNth[T](list:List[T], n:Int) :List[T] = {
     | list.take(n-1):::list.drop(n)
     | }

答案 7 :(得分:0)

另一种方法:为List创建一个完全符合您需求的函数。这与Martin的dropNth函数相同,但不需要O(n)reverse

    import scala.collection.mutable.ListBuffer

    implicit class improvedList[A](xs: List[A]) {
      def filterAllWhereIndex(n: Int): List[A] = {
        var i = 1
        var these = xs
        val b = new ListBuffer[A]
        while (these.nonEmpty) {
          if (i !=  n) {
            b += these.head
            i += 1
          } else i = 1
          these = these.tail
        }
        b.result
      }
    }

    (1 to 5000000).toList filterAllWhereIndex 3

如果你想要有效率,这就可以了。另外,它可以用作中缀运算符,如上所示。这是一个很好的模式,以避免使用zipWithIndex,这似乎有点沉重的时间和空间。