mainas
mainas

Reputation: 754

Scala - finding a specific tuple in a list

Let's say we have this list of tuples:

val data = List(('a', List(1, 0)), ('b', List(1, 1)), ('c', List(0)))

The list has this signature:

List[(Char, List[Int])]

My task is to get the "List[Int]" element from a tuple inside "data" whose key is, for instance, letter "b". If I implement a method like "findIntList(data, 'b')", then I expect List(1, 1) as a result. I have tried the following approaches:

  1. data.foreach { elem => if (elem._1 == char) return elem._2 }
  2. data.find(x=> x._1 == ch)
  3. for (elem <- data) yield elem match {case (x, y: List[Bit]) => if (x == char) y}
  4. for (x <- data) yield if (x._1 == char) x._2

With all the approaches (except Approach 1, where I employ an explicit "return"), I get either a List[Option] or List[Any] and I don't know how to extract the "List[Int]" out of it.

Upvotes: 15

Views: 18197

Answers (5)

M Donkers
M Donkers

Reputation: 26

You can also solve this using pattern matching. Keep in mind you need to make it recursive though. The solution should look something like this;

def findTupleValue(tupleList: List[(Char, List[Int])], char: Char): List[Int] = tupleList match {
  case (k, list) :: _ if char == k => list
  case _ :: theRest => findTupleValue(theRest, char)
}

What this will do is walk your tuple list recursively. Check whether the head element matches your condition (the key you are looking for) and then returns it. Or continues with the remainder of the list.

Upvotes: 0

Jens Schauder
Jens Schauder

Reputation: 81930

One of many ways:

data.toMap.get('b').get

toMap converts a list of 2-tuples into a Map from the first element of the tuples to the second. get gives you the value for the given key and returns an Option, thus you need another get to actually get the list.

Or you can use:

data.find(_._1 == 'b').get._2 

Note: Only use get on Option when you can guarantee that you'll have a Some and not a None. See http://www.scala-lang.org/api/current/index.html#scala.Option for how to use Option idiomatic.

Update: Explanation of the result types you see with your different approaches

Approach 2: find returns an Option[List[Int]] because it can not guarantee that a matching element gets found.

Approach 3: here you basically do a map, i.e. you apply a function to each element of your collection. For the element you are looking for the function returns your List[Int] for all other elements it contains the value () which is the Unit value, roughly equivalent to void in Java, but an actual type. Since the only common super type of ´List[Int]´ and ´Unit´ is ´Any´ you get a ´List[Any]´ as the result.

Approach 4 is basically the same as #3

Upvotes: 22

korefn
korefn

Reputation: 955

You can try something like(when you are sure it exists) simply by adding type information.

val char = 'b'
data.collect{case (x,y:List[Int]) if x == char => y}.head

or use headOption if your not sure the character exists

data.collect{case (x,y:List[Int]) if x == char => y}.headOption

Upvotes: 0

Jatin
Jatin

Reputation: 31724

There are multiple ways of doing it. One more way:

scala> def listInt(ls:List[(Char, List[Int])],ch:Char) = ls filter (a => a._1 == ch) match {
 | case Nil => List[Int]()
 | case x ::xs => x._2
 | }
listInt: (ls: List[(Char, List[Int])], ch: Char)List[Int]
scala> listInt(data, 'b')
res66: List[Int] = List(1, 1)

Upvotes: 1

chris
chris

Reputation: 5028

Another way is

data.toMap.apply('b')

Or with one intermediate step this is even nicer:

val m = data.toMap
m('b')

where apply is used implicitly, i.e., the last line is equivalent to

m.apply('b')

Upvotes: 1

Related Questions