I am having the following iterator:
val it = Iterator(("a",5),("a",3),("a",2),("a",1),("b",8),("b",2),("b",1),("c",1))
The values inside are sorted firstly by the first element(String) and secondly by the second(Int). How can I get the first 2 values from each 'letter'. So the result should be in that example:
Iterator(("a",5),("a",3),("b",8),("b",2),("c",1))
It can be done with groupBy:
it.toList.groupBy(_._1).mapValues(_.take(2)).values.flatten.toIterator
but I would like to see a solution which goes through each element and check the previous 'string' element and if its the same and the 'count' is less than 2 then it yield
this value.
Edit:
Following the logic of @jwvh answer: How can it be generalized to take the first N values instead of the first 2?
It might be nice if we didn't have to consume the entire iterator all at once.
UPDATED
case class LimitItr[A,B](var itr: Iterator[(A,B)], reps:Int) extends Iterator[(A,B)] {
private var memory: List[A] = List()
def hasNext = itr.hasNext
def next() = {
val current = itr.next
if (!memory.headOption.contains(current._1))
memory = List()
memory = current._1 :: memory
if (memory.length >= reps) {
itr = itr.dropWhile(_._1 == memory.head)
itr.hasNext // force the iterator forward
}
current
}
}
Usage:
val repLimitedItr = LimitItr(itrOfTuples, numOfRepetitionsAllowed)
You could use a fold
operation, but it is more cumbersome than your solution:
val result = it.foldLeft((Seq[(String, Int)](), "", 0)){
case ((acc, prev, count), (l, n)) =>
if (prev == l) {
if (count < 2) (acc :+ (l, n), prev, count + 1)
else (acc, prev, count + 1)
}
else (acc :+ (l, n), l, 1)
}
println(result._1)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With