简体   繁体   中英

Scala iterate over list

How can I convert List(1,2,30,13,4) to List(1,2,3,4) without using mutable collection?

30 is a kind of escape number; if found, it should be removed, and next number should be decreased by 10.

You should probably review your code/use case because this kinda smells, anyway:

List(1,2,30,13,4).foldLeft((List.empty[Int], false)) {
  case ((accumulator, wasFound), next) =>
    if(wasFound) (accumulator :+ (next - 10), false)
    else if(next == 30) (accumulator, true)
    else (accumulator :+ next, false)
}._1

Basically you can keep a boolean and an accumulator and use it to remember if a 30 was found or not and take the appropriate action in case it was.

Note that this won't help you if you have something like List(1,3,30,40) , the output would be List(1,3,30) , you should make clear if this case is acceptable or not, if not I would use a recursive solution which will allow to iterate an element twice in case it's a 30:

scala> def loop(list: List[Int], acc: List[Int], wasFound: Boolean, toRemove: Int): List[Int] = list match {
 |     case h :: t =>
 |       if(wasFound) loop((h - 10) :: t, acc, false, toRemove)
 |       else if(h == toRemove) loop(t, acc, true, toRemove)
 |       else loop(t, acc :+ h, false, toRemove)
 |     case Nil =>
 |       acc
 |   }
loop: (list: List[Int], acc: List[Int], wasFound: Boolean, toRemove: Int)List[Int]

scala> loop(List(1,2,30,13,4), List(), false, 30)
res1: List[Int] = List(1, 2, 3, 4)

scala> loop(List(1,2,30,40, 13,4), List(), false, 30)
res2: List[Int] = List(1, 2, 3, 4)

The logic is very similar, the only difference is you iterate twice the one found after the 30, so that if it's another 30 it gets removed and the next decreased.

Zip previous item to each item, and then decide if and what to return using collect and a partial function:

l.zip(l.head :: l).collect {
  case (v, 30) if v != 30 => v - 10  // only previous is 30 - decrease 10
  case (v, prev) if v != 30 => v     // none of them is 30 - just return value
  // skipping (filtering out) case where v == 30
}

For example:

  • for val l = List(1,2,30,13,4,30,15,6) , this would return List(1,2,3,4,5,6)
  • for val l = List(1,3,30,40) , returns List(1,3,30)
  • for val l = List(30,11,2) , returns List(1,2)

Along lines of @Victor comment, and generalizing:

def escapeAdjust[A](xs: List[A], p: A => Boolean, f: A => A): List[A] = {
  @tailrec
  def loop(ys: List[A], p: A => Boolean, f: A => A, acc: List[A]): List[A] = ys match {
    case Nil => acc
    case h :: Nil => if (p(h)) acc else h :: acc
    case h :: next :: rest => if (p(h)) loop(f(next) :: rest, p, f, acc)
                              else loop(next :: rest, p, f, h :: acc)
  }
  loop(xs, p, f, List[A]()).reverse
}

usage:

val p = (x: Int) => x == 30
val f = (y: Int) => y - 10

escapeAdjust(List[Int](1,2,30,13,4), p, f)
res12: List[Int] = List(1, 2, 3, 4)

escapeAdjust(List(1,2,30,13,4,30,15,6), p, f)
res13: List[Int] = List(1, 2, 3, 4, 5, 6)

escapeAdjust(List(1,3,30,40), p, f)
res14: List[Int] = List(1, 3)

escapeAdjust(List(30,11,2), p, f)
res15: List[Int] = List(1, 2)

escapeAdjust(List(1,3,30,40, 5), p, f)
res16: List[Int] = List(1, 3, -5)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM