Home > other >  .contains() method throws java.util.NoSuchElementException
.contains() method throws java.util.NoSuchElementException

Time:01-17

I am trying to remove given elements for n occurences(not all occurrences!) in the list. The problem I'm facing is when I try to branch into two cases in which the list contains the given element or does not. My if statement gives me an error as descripted in the title. How can I resolve this problem?

My code

def removeN[A](xs: List[A], elem: A, n: Int) : List[A] = {
    val elemCount = xs.groupBy(identity).mapValues(_.size)(elem)
    if (xs.contains(elem) == false)
      xs
    else if (elemCount == n) 
      xs.filterNot(x => x == elem)
    else {
      val (left, right) = xs.span(_ != elem)
      print(s"$left and $right")
      left ::: right.tail  
    }
}

Error messages

    removeN(List(1,2,3,2,1), 0, 2)
    
    java.util.NoSuchElementException: key not found: 0
    at scala.collection.MapOps.default(Map.scala:274)
    at scala.collection.MapOps.default$(Map.scala:273)
    at scala.collection.AbstractMapView.default(MapView.scala:186)
    at scala.collection.MapOps.apply(Map.scala:176)
    at scala.collection.MapOps.apply$(Map.scala:175)
    at scala.collection.AbstractMapView.apply(MapView.scala:186)
    at removeN(<console>:3)
    ... 32 elided


Test Case
removeN(List(1,2,3,2,1), 0, 2)  // => List(1, 2, 3, 2, 1)
    

CodePudding user response:

It is not contains which is throwing - you are trying to get the element from map before checking if it will be there at all:

val elemCount = xs.groupBy(identity).mapValues(_.size)(elem)

just count elements in-place:

def removeN[A](xs: List[A], elem: A, n: Int) : List[A] = {
    if (xs.contains(elem) == false) xs
    else if (xs.count(_ == elem) == n) xs.filterNot(x => x == elem)
    else {
        val (left, right) = xs.span(_ != elem)
        print(s"$left and $right")
        left ::: right.tail  
    }
}

CodePudding user response:

I guess, I am missing something, but your function (after fixing the bug mentioned in the other answer) seems to be removing all occurrences if there are exactly n of them, or just the first occurrence otherwise (it is also very inefficient, involving multiple redundant traversals of the list).

Here is a (more efficient) version, removing first N occurrences, assuming that's what you actually were after:

def removeN[A](xs: List[A], elem: A, n: Int, result: List[A]=Nil): List[A] = xs match {
    case Nil => result.reverse
    case `elem`::tail if n > 0 => removeN(tail, elem, n-1 result)
    case head::tail => removeN(tail, elem, n, head::result)
 }

This can be marginally improved further by adding a "shortcut" special case for n==0 && result == Nil, but doesn't seem worth the effort and loss of readability.

  • Related