KurryF
KurryF

Reputation: 91

How to get distinct values from an element within list of tuples in Scala

Hello I have this exercise where I am trying to find all numbers that do not exist in the second element of a tuple, and print them out in a list. I have a working version but it returns duplicate values, and it is not being returned in a list. I cant seem to use distinct here. Can any one explain to me why distinct cannot be used here and what i would need to do instead. Also if there is a better way to get the answer to the exercise, that would be appreciated.

 object Example{

   def main(args: Array[String]): Unit = {
       val tupleExercise: List[(Int,Int)] = List(
         (1, 3),
         (2, 3),
         (3, 6),
         (5, 6),
         (5, 7),
         (4, 5),
         (4, 8),
         (4, 9),
         (9, 11)
       )

     def notExistsInSecond(n: List[(Int, Int)]): Unit = {
       var potential =
         for (a <- n) {
           for (c <- n) 
             if(c._1 == a._2) println(a._1)
         }
     }
     println(notExistsInSecond(tupleExercise))
   }
 }
//expected ouput
// [1, 2, 4]

Upvotes: 0

Views: 364

Answers (3)

Raphael Roth
Raphael Roth

Reputation: 27383

I don't think there is a (performant) way to write it in one line, but you could write it like this:

def notExistsInSecond(n: List[(Int, Int)]): List[Int] = {
  val second = n.map(_._2).distinct
  n.map(_._1).distinct.filterNot(second.contains)
}

It gets probabely more performant if you use a HashSet instead of a List for the lookup:

def notExistsInSecond(n: List[(Int, Int)]): List[Int] = {
  val valuesSet = n.map(_._2).toSet
  n.map(_._1).distinct.filterNot(valuesSet.contains)
}

Upvotes: 3

The same logic as Raphael but more performant.

def notExistsInSecond[A](n: List[(A, A)]): List[A] = {
  val seconds = n.iterator.map(_._2).toSet

  val (result, _) = n.foldLeft(List.empty[Int] -> Set.empty[Int]) {
    case ((result, added), (e, _)) =>
      if (!seconds(e) && !added(e))
        (e :: result) -> (added + e)
      else
        result -> added
  }

  result.reverse // If you do not care about the order you can remove this reverse
}

Or if you do not care about the result being a list, this would be even faster:

def notExistsInSecond[A](n: List[(A, A)]): Set[A] = {
  val seconds = n.iterator.map(_._2).toSet

  n.iterator.collect {
    case (e, _) if (!seconds(e)) => e
  }.toSet
}

Upvotes: 1

jwvh
jwvh

Reputation: 51271

Here's a way to do it with a single pass through the input list and using Set to insure distinct results.

def notExistsInSecond(n: List[(Int, Int)]): List[Int] = {
  val (s1,s2) = n.foldLeft((Set.empty[Int],Set.empty[Int])){
    case ((sa,sb),(a,b)) => (sa+a, sb+b)
  }
  (s1 diff s2).toList
}

Upvotes: 1

Related Questions