Convert Loop problem to Recursive Solution in Scala - scala

I've written the following method (working perfectly fine) which takes a list and returns a list of lists containing the
elements, so that the first list contains half of list’s elements, the next contains half of the remaining elements, and so on. For example,
repHalve(List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15))
Returning:
List(List(1, 2, 3, 4, 5, 6, 7, 8), List(9, 10, 11, 12), List(13, 14), List(15))
The question is I'm new to Scala and wanted to convert this method to recursive approach. Please let me know how should I convert this. I know the base case could be the same as the condition inside while loop but still unable to figure it out. Any help would be highly appreciated. Thanks
def repHalve(list:List[Any]){
var local_list:List[Any] = list
var list_of_lists:List[Any] = List.empty
while(local_list.length>1){
val sub = local_list.slice(0, (local_list.length/2)+1)
list_of_lists ++= List(sub)
local_list = local_list.slice(local_list.length/2+1, local_list.length)
}
list_of_lists ++= List(List(list.last))
println(list_of_lists)
}

Consider analogous solution to Luis' but using splitAt
def repHalve(l: List[Int]): List[List[Int]] = {
def half(i: Int): Int = if ((i % 2) == 0) i / 2 else (i + 1) / 2
#annotation.tailrec
def loop(l: List[Int], size: Int, acc: List[List[Int]]): List[List[Int]] = l match {
case x :: Nil => (List(x) :: acc).reverse
case _ =>
val (left, right) = l.splitAt(half(size))
loop(right, right.size, left :: acc)
}
loop(l, l.size, Nil)
}
jmh benchmark using (1 to 200).toList as input indicates Luis' solution is faster
[info] So60178352._luis thrpt 5 666357.490 ± 165323.129 ops/s
[info] So60178352._mario thrpt 5 591174.959 ± 118097.426 ops/s

Here is a completely tail-recursive implementation.
Let me know if you have any questions.
def repHalve[T](list: List[T]): List[List[T]] = {
def half(i: Int): Int =
if ((i % 2) == 0) i / 2 else (i + 1) / 2
#annotation.tailrec
def loop(remaining: List[T], targetLength: Int, acc: List[List[T]]): List[List[T]] =
remaining match {
case Nil => acc.reverse
case list =>
#annotation.tailrec
def innerLoop(remaining: List[T], currentLength: Int, acc: List[T]): (List[T], List[T]) =
remaining match {
case x :: xs =>
if (currentLength != targetLength)
innerLoop(remaining = xs, currentLength + 1, x :: acc)
else
(x :: xs, acc.reverse)
case Nil =>
(Nil, acc.reverse)
}
val (remaining, newList) = innerLoop(remaining = list, currentLength = 0, acc = List.empty)
loop(remaining, half(targetLength), newList :: acc)
}
loop(remaining = list, targetLength = half(list.length), acc = List.empty)
}
Which you can use like this:
repHalve((1 to 20).toList)
// res: List[List[Int]] = List(List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10), List(11, 12, 13, 14, 15), List(16, 17, 18), List(19, 20))

Related

Average for adjacent items in scala

I have a seq
val seq = Seq(1, 9, 5, 4, 3, 5, 5, 5, 8, 2)
I want to get an average for each adjacent (left and right) numbers, meaning in the above example to have the following calculations:
[(1+9)/2, (1+9+5)/3, (9+5+4)/3, (5+4+3)/3, (4+3+5)/3, (3+5+5)/3, (5+5+5)/3, (5+5+8)/3, (5+8+2)/3, (8+2)/2]
The other examples are:
Seq() shouldBe Seq()
Seq(3) shouldBe Seq(3.0d)
Seq(1, 4) shouldBe Seq(2.5d, 2.5d)
Seq(1, 9, 5, 4, 3, 5, 5, 5, 8, 2) shouldBe Seq(5.0, 5.0, 6.0, 4.0, 4.0, 13.0 / 3, 5.0, 6.0, 5.0, 5.0)
I was able to get: numbers.sliding(2, 1).map(nums => nums.sum.toDouble / nums.length).toSeq. But it doesn't consider the previous value.
I tried to do it with foldLeft - it is also cumbersome.
Is there an easy way to do this? What am I missing?
Being honest, this is the kind of problems that I believe are easier to solve using a simple (albeit a bit long) tail-recursive algorithm.
def adjacentAverage(data: List[Int]): List[Double] = {
#annotation.tailrec
def loop(remaining: List[Int], acc: List[Double], previous: Int): List[Double] =
remaining match {
case x :: y :: xs =>
loop(
remaining = y :: xs,
((previous + x + y).toDouble / 3.0d) :: acc,
previous = x
)
case x :: Nil =>
(((previous + x).toDouble / 2.0d) :: acc).reverse
}
data match {
case x :: y :: xs => loop(remaining = y :: xs, acc = ((x + y).toDouble / 2.0d) :: Nil, previous = x)
case x :: Nil => x.toDouble :: Nil
case Nil => Nil
}
}
You can see it running here.
What if you want a sliding window of a different size, like maybe 4 or 7 or ...? The challenge is getting the build-up, (1), (1,2), (1,2,3), (1,2,3,4), ... and the tail-off, ..., (6,7,8,9), (7,8,9), (8,9), (9).
def windowAvg(input: Seq[Int], windowSize: Int): Seq[Double] =
if (input.isEmpty || windowSize < 1) Seq()
else {
val windows = input.sliding(windowSize).toSeq
val buildUp = windows.head.inits.toSeq.tail.reverse.tail
val tailOff = windows.last.tails.toSeq.tail.init
(buildUp ++ windows ++ tailOff).map(x => x.sum.toDouble / x.length)
}
If you really need to trim off the opening and ending single-number entries in the result, then I'll leave that as an exercise for the reader.
My cumbersome solution through foldLeft (no rocket science)
def adjacentAverage(numbers: Seq[Int]): Seq[Double] = numbers.foldLeft(("x", Seq[Double](), 0)) {(acc, num) => acc._1 match {
case "x" => if (numbers.isEmpty) ("x", Seq(), acc._3 + 1) else if (numbers.length == 1) ("x", Seq(num.toDouble), acc._3 + 1) else (num.toString, acc._2 :+ ((num.toDouble + numbers(acc._3 + 1).toDouble) / 2.0), acc._3 + 1)
case _ => (num.toString, try {acc._2 :+ ((acc._1.toDouble + num.toDouble + numbers(acc._3 + 1).toDouble) / 3.0)} catch {case e: IndexOutOfBoundsException => acc._2 :+ ((acc._1.toDouble + num.toDouble) / 2.0) }, acc._3 + 1)
}}._2

How do you rotate (circular shift) of a Scala collection

I can do this quite easily, and cleanly, using a for loop. For instance, if I wanted to traverse a Seq from every element back to itself I would do the following:
val seq = Seq(1,2,3,4,5)
for (i <- seq.indices) {
for (j <- seq.indices) {
print(seq(i + j % seq.length))
}
}
But as I'm looking to fold over the collection, I'm wondering if there is a more idiomatic approach. A recursive approach would allow me to avoid any vars. But basically, I'm wondering if something like the following is possible:
seq.rotatedView(i)
Which would create a rotated view, like rotating bits (or circular shift).
Is it like below:
scala> def rotatedView(i:Int)=Seq(1,2,3,4,5).drop(i)++Seq(1,2,3,4,5).take(i)
rotatedView: (i: Int)Seq[Int]
scala> rotatedView(1)
res48: Seq[Int] = List(2, 3, 4, 5, 1)
scala> rotatedView(2)
res49: Seq[Int] = List(3, 4, 5, 1, 2)
This ought to do it in a fairly generic way, and allow for arbitrary rotations:
def rotateLeft[A](seq: Seq[A], i: Int): Seq[A] = {
val size = seq.size
seq.drop(i % size) ++ seq.take(i % size)
}
def rotateRight[A](seq: Seq[A], i: Int): Seq[A] = {
val size = seq.size
seq.drop(size - (i % size)) ++ seq.take(size - (i % size))
}
The idea is simple enough, to rotate left, drop the first i elements from the left, and take them again from the left to concatenate them in the opposite order. If you don't mind calculating the size of the collection, you can do your operations modulo the size, to allow i to be arbitrary.
scala> rotateRight(seq, 1)
res34: Seq[Int] = List(5, 1, 2, 3, 4)
scala> rotateRight(seq, 7)
res35: Seq[Int] = List(4, 5, 1, 2, 3)
scala> rotateRight(seq, 70)
res36: Seq[Int] = List(1, 2, 3, 4, 5)
Similarly, you can use splitAt:
def rotateLeft[A](seq: Seq[A], i: Int): Seq[A] = {
val size = seq.size
val (first, last) = seq.splitAt(i % size)
last ++ first
}
def rotateRight[A](seq: Seq[A], i: Int): Seq[A] = {
val size = seq.size
val (first, last) = seq.splitAt(size - (i % size))
last ++ first
}
To make it even more generic, using the enrich my library pattern:
import scala.collection.TraversableLike
import scala.collection.generic.CanBuildFrom
implicit class TraversableExt[A, Repr <: TraversableLike[A, Repr]](xs: TraversableLike[A, Repr]) {
def rotateLeft(i: Int)(implicit cbf: CanBuildFrom[Repr, A, Repr]): Repr = {
val size = xs.size
val (first, last) = xs.splitAt(i % size)
last ++ first
}
def rotateRight(i: Int)(implicit cbf: CanBuildFrom[Repr, A, Repr]): Repr = {
val size = xs.size
val (first, last) = xs.splitAt(size - (i % size))
last ++ first
}
}
scala> Seq(1, 2, 3, 4, 5).rotateRight(2)
res0: Seq[Int] = List(4, 5, 1, 2, 3)
scala> List(1, 2, 3, 4, 5).rotateLeft(2)
res1: List[Int] = List(3, 4, 5, 1, 2)
scala> Stream(1, 2, 3, 4, 5).rotateRight(1)
res2: scala.collection.immutable.Stream[Int] = Stream(5, ?)
Keep in mind these are not all necessarily the most tuned for performance, and they also can't work with infinite collections (none can).
Following the OP's comment that they want to fold over it, here's a slightly different take on it that avoids calculating the length of the sequence first.
Define an iterator that will iterate over the rotated sequence
class RotatedIterator[A](seq: Seq[A], start: Int) extends Iterator[A] {
var (before, after) = seq.splitAt(start)
def next = after match {
case Seq() =>
val (h :: t) = before; before = t; h
case h :: t => after = t; h
}
def hasNext = after.nonEmpty || before.nonEmpty
}
And use it like this:
val seq = List(1, 2, 3, 4, 5)
val xs = new RotatedIterator(seq, 2)
println(xs.toList) //> List(3, 4, 5, 1, 2)
A simple method is to concatenate the sequence with itself and then take the slice that is required:
(seq ++ seq).slice(start, start + seq.length)
This is just a variant of the drop/take version but perhaps a little clearer.
Given:
val seq = Seq(1,2,3,4,5)
Solution:
seq.zipWithIndex.groupBy(_._2<3).values.flatMap(_.map(_._1))
or
seq.zipWithIndex.groupBy(_._2<3).values.flatten.map(_._1)
Result:
List(4, 5, 1, 2, 3)
If rotation is more than length of collection - we need to use rotation%length, if negative than formula (rotation+1)%length and take absolute value.
It's not efficient
Another tail-recursive approach. When I benchmarked it with JMH it was about 2 times faster than solution based on drop/take:
def rotate[A](list: List[A], by: Int): List[A] = {
#tailrec
def go(list: List[A], n: Int, acc: List[A]): List[A] = {
if(n > 0) {
list match {
case x :: xs => go(xs, n-1, x :: acc)
}
} else {
list ++ acc.reverse
}
}
if (by < 0) {
go(list, -by % list.length, Nil)
} else {
go(list, list.length - by % list.length, Nil)
}
}
//rotate right
rotate(List(1,2,3,4,5,6,7,8,9,10), 3) // List(8, 9, 10, 1, 2, 3, 4, 5, 6, 7)
//use negative number to rotate left
rotate(List(1,2,3,4,5,6,7,8,9,10), -3) // List(4, 5, 6, 7, 8, 9, 10, 1, 2, 3)
Here is one liner solution
def rotateRight(A: Array[Int], K: Int): Array[Int] = {
if (null == A || A.size == 0) A else (A drop A.size - (K % A.size)) ++ (A take A.size - (K % A.size))
}
rotateRight(Array(1,2,3,4,5), 3)
Here's a fairly simple and idiomatic Scala collections way to write it:
def rotateSeq[A](seq: Seq[A], isLeft: Boolean = false, count: Int = 1): Seq[A] =
if (isLeft)
seq.drop(count) ++ seq.take(count)
else
seq.takeRight(count) ++ seq.dropRight(count)
We can simply use foldLeft to reverse a list as below.
val input = List(1,2,3,4,5)
val res = input.foldLeft(List[Int]())((s, a) => { List(a) ++: s})
println(res) // List(5, 4, 3, 2, 1)
Another one line solution if you don't need to validate the "offset":
def rotate[T](seq: Seq[T], offset: Int): Seq[T] = Seq(seq, seq).flatten.slice(offset, offset + seq.size)
This is a simple piece of code
object tesing_it extends App
{
val one = ArrayBuffer(1,2,3,4,5,6)
val i = 2 //the number of index you want to move
for(z<-0 to i){
val y = 0
var x = one += one(y)
x = x -= x(y)
println("for seq after process " +z +" " + x)
}
println(one)
}
Result:
for seq after process 0 ArrayBuffer(2, 3, 4, 5, 6, 1)
for seq after process 1 ArrayBuffer(3, 4, 5, 6, 1, 2)
for seq after process 2 ArrayBuffer(4, 5, 6, 1, 2, 3)
ArrayBuffer(4, 5, 6, 1, 2, 3)

How to replace values in one Seq with values from another Seq in Scala?

I have sequences:
val A = Seq(1,3,0,4,2,0,7,0,6)
val B = Seq(8,9,10)
I need a new sequence where 0 are replaced with values from second sequence:
Seq(1,3,8,4,2,9,7,10,6)
How to do that in functional style?
You can use map here, by replacing all 0s with the next element of b (by converting b to an iterator, and using next):
val a = Seq(1,3,0,4,2,0,7,0,6)
val b = Seq(8,9,10).iterator
a.map { e => if (e == 0) b.next else e } //Seq(1,3,8,4,2,9,7,10,6)
Not sure that iterators are really functional. Anyway, here's an alternative
val A = Seq(1,3,0,4,2,0,7,0,6)
val B = Seq(8,9,10)
A.foldLeft((B, Seq[Int]())) {case ((b, r), e) =>
if (e == 0 && ! b.isEmpty) (b.tail, b.head +: r) else (b, e +: r) }
._2.reverse
//> res0: Seq[Int] = List(1, 3, 8, 4, 2, 9, 7, 10, 6)
EDIT: Updated per the comment to leave the zeros if we're out of elements of B
EDIT2:
A pattern-matching variant is neater:
A.foldLeft((B, Seq[Int]())){case ((h +: t, r), 0) => (t, h +: r)
case ((b, r), e) => (b, e +: r)}
._2.reverse
And, based on what is proper monad or sequence comprehension to both map and carry state across?
A.mapAccumLeft(B, { case ((h +: t), 0) => (t, h)
case (b, e) => (b, e) }
(probably, I don't have scalaz installed to test it)
If you want to look at Tail Recursion then this suits you.
#annotation.tailrec
def f(l1: Seq[Int], l2: Seq[Int], res: Seq[Int] = Nil): Seq[Int] = {
if (l1 == Nil) res
else {
if (l1.head == 0 && l2 != Nil) f(l1.tail, l2.tail, res :+ l2.head)
else
f(l1.tail, l2, res :+ l1.head)
}
}
val A = Seq(1, 3, 0, 4, 2, 0, 7, 0, 6)
val B = Seq(8, 9, 10)
scala> f(A,B)
res0: Seq[Int] = List(1, 3, 8, 4, 2, 9, 7, 10, 6)
if you run out of elements in B then ,
val A = Seq(1, 3, 0, 4, 2, 0, 7, 0, 6)
val B = Seq(8, 9)
scala> f(A,B)
res1: Seq[Int] = List(1, 3, 8, 4, 2, 9, 7, 0, 6)
if elements in A are less than B then,
val A = Seq(1, 0)
val B = Seq(8, 9, 10)
scala> f(A,B)
res2: Seq[Int] = List(1, 8)

Group list of numbers into groups based on the range from the lowest number

Assuming there the list of numbers and a range value, I want to group them into groups, in which the item in each group is within the range from the lowest number, and sort them.
For example, I have a list val l = List(1,2,3,4,5,6,7,8,9,10) and the range val range = 2. Then, I'm looking for a way to output the following result: result = List(List(1,2,3), List(4,5,6), List(7,8,9), List(10)). Which means if range = 0 then only identical numbers are in the same group.
At the moment, I use the following method
val minVal = l.min
val range1 = (minVal + range).toDouble
val groups = l.foldLeft(Map[Int, List[Int]]())((result, num) => {
val numRange = math.ceil(num / range1).toInt
if (result.contains(numRange)) {
result.updated(numRange, num :: result(numRange))
} else {
result.updated(numRange, List(num))
}
})
groups.keys.toList.sortBy(k => k).map(groups(_))
It works in most cases except when range = 0 and slowestNum != 1. E.g. for the list val l = List(2,3,4,5,6,7,8,9,10) and the range val range = 2, the result is List(List(2), List(4, 3), List(6, 5), List(8, 7), List(10, 9)).
So, I wonder if there is any other way to solve this problem.
Why complicate?
def coll(l: List[Int], range: Int): List[List[Int]] =
if (l.isEmpty) Nil else {
val (b, a) = l.span((l.head - range to l.head + range).contains)
b :: coll(a, range)
}
So, this algorithm collects numbers into a group until the number are in the plus/minus range.
val list = List(7,4,1,9,10,20,50,52,30)
coll(list, 3)
res6: List[List[Int]] = List(List(7, 4), List(1), List(9, 10), List(20), List(50, 52), List(30))
If you want each group by itself sorted, then call res6.map(_.sorted).
I would personally do something like this:
val l = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
val range = 2
val result = l.sorted.foldLeft(List[List[Int]]()) {
(cur, x) =>
if ((cur nonEmpty) && x - cur.head.last <= range) (x :: cur.head) :: cur.tail
else List(x) :: cur
}
although there may be some clever and neat ways. Of course, you can always do if you want the result ordered:
val ret = result.reverse.map(_.reverse)
Hope it helped!
Try something like this
val groupedList = l.map(i => l.filter(s => s >= i && s - i <= range))
groupedList.foldLeft(List(groupedList.head)) {
case (r, c) => if (r.last.last < c.head) r ++ List(c) else r
}
For range 2
val l = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
val range = 2
val groupedList = l.map(i => l.filter(s => s >= i && s - i <= range))
groupedList.foldLeft(List(groupedList.head)) {
case (r, c) => if (r.last.last < c.head) r ++ List(c) else r
}
//> res0: List[List[Int]] = List(List(1, 2, 3), List(4, 5, 6), List(7, 8, 9), List(10))
For range 0
val l = List(1,1,1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
val range = 0
val groupedList = l.map(i => l.filter(s => s >= i && s - i <= range))
groupedList.foldLeft(List(groupedList.head)) {
case (r, c) => if (r.last.last < c.head) r ++ List(c) else r
}
//> res0: List[List[Int]] = List(List(1, 1, 1), List(2), List(3), List(4), List(5), List(6), List(7), List(8), List(9), List(10))

How to remove 2 or more duplicates from list and maintain their initial order?

Lets assume we have a Scala list:
val l1 = List(1, 2, 3, 1, 1, 3, 2, 5, 1)
We can easily remove duplicates using the following code:
l1.distinct
or
l1.toSet.toList
But what if we want to remove duplicates only if there are more than 2 of them? So if there are more than 2 elements with the same value we remain only two and remove the rest of them.
I could achieve it with following code:
l1.groupBy(identity).mapValues(_.take(2)).values.toList.flatten
that gave me the result:
List(2, 2, 5, 1, 1, 3, 3)
Elements are removed but the order of remaining elements is different from how these elements appeared in the initial list. How to do this operation and remain the order from original list?
So the result for l1 should be:
List(1, 2, 3, 1, 3, 2, 5)
Not the most efficient.
scala> val l1 = List(1, 2, 3, 1, 1, 3, 2, 5, 1)
l1: List[Int] = List(1, 2, 3, 1, 1, 3, 2, 5, 1)
scala> l1.zipWithIndex.groupBy( _._1 ).map(_._2.take(2)).flatten.toList.sortBy(_._2).unzip._1
res10: List[Int] = List(1, 2, 3, 1, 3, 2, 5)
My humble answer:
def distinctOrder[A](x:List[A]):List[A] = {
#scala.annotation.tailrec
def distinctOrderRec(list: List[A], covered: List[A]): List[A] = {
(list, covered) match {
case (Nil, _) => covered.reverse
case (lst, c) if c.count(_ == lst.head) >= 2 => distinctOrderRec(list.tail, covered)
case _ => distinctOrderRec(list.tail, list.head :: covered)
}
}
distinctOrderRec(x, Nil)
}
With the results:
scala> val l1 = List(1, 2, 3, 1, 1, 3, 2, 5, 1)
l1: List[Int] = List(1, 2, 3, 1, 1, 3, 2, 5, 1)
scala> distinctOrder(l1)
res1: List[Int] = List(1, 2, 3, 1, 3, 2, 5)
On Edit: Right before I went to bed I came up with this!
l1.foldLeft(List[Int]())((total, next) => if (total.count(_ == next) >= 2) total else total :+ next)
With an answer of:
res9: List[Int] = List(1, 2, 3, 1, 3, 2, 5)
Not the prettiest. I look forward to seeing the other solutions.
def noMoreThan(xs: List[Int], max: Int) =
{
def op(m: Map[Int, Int], a: Int) = {
m updated (a, m(a) + 1)
}
xs.scanLeft( Map[Int,Int]().withDefaultValue(0) ) (op).tail
.zip(xs)
.filter{ case (m, a) => m(a) <= max }
.map(_._2)
}
scala> noMoreThan(l1, 2)
res0: List[Int] = List(1, 2, 3, 1, 3, 2, 5)
More straightforward version using foldLeft:
l1.foldLeft(List[Int]()){(acc, el) =>
if (acc.count(_ == el) >= 2) acc else el::acc}.reverse
Similar to how distinct is implemeted, with a multiset instead of a set:
def noMoreThan[T](list : List[T], max : Int) = {
val b = List.newBuilder[T]
val seen = collection.mutable.Map[T,Int]().withDefaultValue(0)
for (x <- list) {
if (seen(x) < max) {
b += x
seen(x) += 1
}
}
b.result()
}
Based on experquisite's answer, but using foldLeft:
def noMoreThanBis(xs: List[Int], max: Int) = {
val initialState: (Map[Int, Int], List[Int]) = (Map().withDefaultValue(0), Nil)
val (_, result) = xs.foldLeft(initialState) { case ((count, res), x) =>
if (count(x) >= max)
(count, res)
else
(count.updated(x, count(x) + 1), x :: res)
}
result.reverse
}
distinct is defined for SeqLike as
/** Builds a new $coll from this $coll without any duplicate elements.
* $willNotTerminateInf
*
* #return A new $coll which contains the first occurrence of every element of this $coll.
*/
def distinct: Repr = {
val b = newBuilder
val seen = mutable.HashSet[A]()
for (x <- this) {
if (!seen(x)) {
b += x
seen += x
}
}
b.result()
}
We can define our function in very similar fashion:
def distinct2[A](ls: List[A]): List[A] = {
val b = List.newBuilder[A]
val seen1 = mutable.HashSet[A]()
val seen2 = mutable.HashSet[A]()
for (x <- ls) {
if (!seen2(x)) {
b += x
if (!seen1(x)) {
seen1 += x
} else {
seen2 += x
}
}
}
b.result()
}
scala> distinct2(l1)
res4: List[Int] = List(1, 2, 3, 1, 3, 2, 5)
This version uses internal state, but is still pure. It is also quite easy to generalise for arbitrary n (currently 2), but specific version is more performant.
You can implement the same function with folds carrying the "what is seen once and twice" state with you. Yet the for loop and mutable state does the same job.
How about this:
list
.zipWithIndex
.groupBy(_._1)
.toSeq
.flatMap { _._2.take(2) }
.sortBy(_._2)
.map(_._1)
Its a bit ugly, but its relatively faster
val l1 = List(1, 2, 3, 1, 1, 3, 2, 5, 1)
l1.foldLeft((Map[Int, Int](), List[Int]())) { case ((m, ls), x) => {
val z = m + ((x, m.getOrElse(x, 0) + 1))
(z, if (z(x) <= 2) x :: ls else ls)
}}._2.reverse
Gives: List(1, 2, 3, 1, 3, 2, 5)
Here is a recursive solution (it will stack overflow for large lists):
def filterAfter[T](l: List[T], max: Int): List[T] = {
require(max > 1)
//keep the state of seen values
val seen = Map[T, Int]().withDefaultValue(0)//init to 0
def filterAfter(l: List[T], seen: Map[T, Int]): (List[T], Map[T, Int]) = {
l match {
case x :: xs =>
if (seen(x) < max) {
//Update the state and pass to next
val pair = filterAfter(xs, seen updated (x, seen(x) + 1))
(x::pair._1, pair._2)
} else {
//already seen more than max
filterAfter(xs, seen)
}
case _ => (l, seen)//empty, terminate recursion
}
}
//call inner recursive function
filterAfter(l, seen, 2)._1
}
Here is canonical Scala code to do reduce three or more in a row to two in a row:
def checkForTwo(candidate: List[Int]): List[Int] = {
candidate match {
case x :: y :: z :: tail if x == y && y == z =>
checkForTwo(y :: z :: tail)
case x :: tail =>
x :: checkForTwo(tail)
case Nil =>
Nil
}
}
It looks at the first three elements of the list, and if they are the same, drops the first one and repeats the process. Otherwise, it passes items on through.
Solution with groupBy and filter, without any sorting (so it's O(N), sorting will give you additional O(Nlog(N)) in typical case):
val li = l1.zipWithIndex
val pred = li.groupBy(_._1).flatMap(_._2.lift(1)) //1 is your "2", but - 1
for ((x, i) <- li if !pred.get(x).exists(_ < i)) yield x
I prefer approach with immutable Map:
def noMoreThan[T](list: List[T], max: Int): List[T] = {
def go(tail: List[T], freq: Map[T, Int]): List[T] = {
tail match {
case h :: t =>
if (freq(h) < max)
h :: go(t, freq + (h -> (freq(h) + 1)))
else go(t, freq)
case _ => Nil
}
}
go(list, Map[T, Int]().withDefaultValue(0))
}