Reputation: 869
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.
Upvotes: 2
Views: 487
Reputation: 126
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)
Upvotes: 0
Reputation: 37852
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:
val l = List(1,2,30,13,4,30,15,6)
, this would return List(1,2,3,4,5,6)
val l = List(1,3,30,40)
, returns List(1,3,30)
val l = List(30,11,2)
, returns List(1,2)
Upvotes: 2
Reputation: 15783
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.
Upvotes: 3