Mpizos Dimitris
Mpizos Dimitris

Reputation: 4991

Iterating in Scala: checking previous values

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?

Upvotes: 4

Views: 2546

Answers (3)

jwvh
jwvh

Reputation: 51271

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)

Upvotes: 4

Nyavro
Nyavro

Reputation: 8866

Another solution using foldLeft could be:

it.foldLeft (List[(String, Int)]()) {
  case (acc, (k,v)) if acc.takeWhile(_._1==k).size<2 => (k,v)::acc
  case (acc, _) => acc
}.reverse

Upvotes: 3

devkat
devkat

Reputation: 1644

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)

Upvotes: 4

Related Questions