Scala filter on a list by index

不问归期 提交于 2019-12-21 12:29:16

问题


I wanted to write it functionally, and the best I could do was:

list.zipWithIndex.filter((tt:Tuple2[Thing,Int])=>(tt._2%3==0)).unzip._1

to get elements 0, 3, 6,...

Is there a more readable Scala idiom for this?


回答1:


If efficiency is not an issue, you could do the following:

list.grouped(3).map(_.head)

Note that this constructs intermediate lists.

Alternatively you can use a for-comprehension:

for {
  (x,i) <- list zipWithIndex
  if i % 3 == 0
} yield x

This is of course almost identical to your original solution, just written differently.

My last alternative for you is the use of collect on the zipped list:

list.zipWithIndex.collect {
  case (x,i) if i % 3 == 0 => x
}



回答2:


Not much clear, but still:

xs.indices.collect { case i if i % 3 == 0 => xs(i) }



回答3:


A nice, functional solution, without creating temporary vectors, lists, and so on:

def everyNth[T](xs: List[T], n:Int): List[T] = xs match {
  case hd::tl => hd::everyNth(tl.drop(n-1), n)
  case Nil => Nil
}



回答4:


Clojure has a take-nth function that does what you want, but I was surprised to find that there's not an equivalent method in Scala. You could code up a similar recursive solution based off the Clojure code, or you could read this blog post:

Scala collections: Filtering each n-th element

The author actually has a nice graph at the end showing the relative performance of each of his solutions.




回答5:


I would do it like in Octave mathematical program.

val indices = 0 until n by 3  // Range 0,3,6,9 ...

and then I needed some way to select the indices from a collection. Obviously I had to have a collection with random-access O(1). Like Array or Vector. For example here I use Vector. To wrap the access into a nice DSL I'd add an implicit class:

implicit class VectorEnrichedWithIndices[T](v:Vector[T]) {
  def apply(indices:TraversableOnce[Int]):Vector[T] = {
    // some implementation 
    indices.toVector.map(v)
  }
}

The usage would look like:

val vector = list.toVector
val every3rdElement = vector(0 until vector.size by 3)



回答6:


Ah, how about this?

val l = List(10,9,8,7,6,5,4,3,2,1,0)
for (i <- (0 to l.size - 1 by 3).toList) yield l(i)
//res0: List[Int] = List(10, 7, 4, 1)

which can be made more general by

def seqByN[A](xs: Seq[A], n: Int): Seq[A] = for (i <- 0 to xs.size - 1 by n) yield xs(i)

scala> seqByN(List(10,9,8,7,6,5,4,3,2,1,0), 3)
res1: Seq[Int] = Vector(10,7,4,1)

scala> seqByN(List(10,9,8,7,6,5,4,3,2,1,0), 3).toList
res2: Seq[Int] = List(10,7,4,1)

scala> seqByN(List[Int](), 3)
res1: Seq[Int] = Vector()

But by functional do you mean only using the various List combinator functions? Otherwise, are Streams functional enough?

def fromByN[A](xs: List[A], n: Int): Stream[A] = if (xs.isEmpty) Stream.empty else
  xs.head #:: fromByN(xs drop n, n)

scala> fromByN(List(10,9,8,7,6,5,4,3,2,1,0), 3).toList
res17: List[Int] = List(10, 7, 4, 1)


来源:https://stackoverflow.com/questions/18814522/scala-filter-on-a-list-by-index

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!