rjc
rjc

Reputation: 2915

How to declare tuple as the return type of a function and how to access tuple in caller code in scala?

Suppose I have a scala function that is supposed to return a tuple of type (String, Int, Int) mapped to keys (words, row, col):

def getResult(param: Int)    {

// a lot of logic goes here to generate tuple values

// now return the tuple

}

In caller code:

var words, row, col
if(someValue) {

  getResults(someValue)  // assigns the tuple values to keys words, row and col
  // reference words, row and col variables here

} else
  getResults(someOtherValue)  // assigns the tuple values to keys words, row and col
// reference words, row and col variables here
}
// in this scope here words, row and col are defined  and must have values that got assigned in above code
// do something more with words, row and col variables.

The code is incomplete. So how would I declare and return the tuple in the function and how would I use in above scenario?

Is tuple recommended way in above case even though map seems better fit?

Despite all the answers I got, no answer has addressed the issue of how do I declare tuple and fill the tuple later in the code ( not assigning values to tuple at the time declaration like this answer suggests:

var (words, row, col) =  if(someValue) {
  getResults(someValue)
} else {
  getResults(someOtherValue)
}

This is the part of the code that remains unanswered:

var words, row, col  // how do I delcare tuple here?
    if(someValue) {

      getResults(someValue)  // assigns the tuple values to keys words, row and col
             // how I do that here ?
      // reference words, row and col variables here

    } else
      getResults(someOtherValue)  // assigns the tuple values to keys words, row and col
    // reference words, row and col variables here
    }

Upvotes: 8

Views: 19055

Answers (5)

Shadowlands
Shadowlands

Reputation: 15074

Am I right in assuming you wish to declare your result variables first (and need to access them outside the if/else construct), then call code that sets and uses them (inside the if/else construct)? If so, it should be as simple as

var words: String = _
var row: Int = _
var col: Int = _
...
if (someValue) {
  val (words, row, col) = getResults(someValue)
  // use words, row, col
  // Note: keyword 'val' added per comments by Prometheus and John Threepwood below
} else {
  val (words, row, col) = getResults(someOtherValue)
  // use words, row, col
  // Note: keyword 'val' added per comments by Prometheus and John Threepwood below
}

Upvotes: 2

Rex Kerr
Rex Kerr

Reputation: 167891

First, you need to decide whether you need a map.

You might want a map because

  1. You get different key-value pairs each time
  2. You have awkwardly many key-value pairs and need to iterate over them to manage them
  3. You have key values as data, not just as part of your code, and need to retrieve the corresponding value

So far, it doesn't look like any of these three things are the case. So you don't really need a map, you just need a name for your different types of data. (That is, you can let the compiler handle the mapping between your name and the corresponding data.)

The most direct way to get named values is with a case class:

case class Result(words: String, row: Int, col: Int) {}

You can return this:

def getResult = Result("an example", 1, 10)

You can assign this:

val result = getResult

and look at the parts:

println(result.words)  // Prints "an example"

You can assign separate variables to the pieces:

val Result(w,r,c) = getResult    // Now w=="an example", r==1, w==10

You can pattern match to find partial answers:

getResult match {
  case Result(_,1,c) => println("Had 1 row and "+c+" columns)
  case _ => println("I don't know what to do with this")
}

You can copy and change by name:

getResult.copy(words = "another example")

and so on.

If you don't need names, you can use tuples which work the same way but only know about the position of the arguments. The first item is called _1, the second _2, and so on. You specify these simply by listing the items between parentheses:

def getResult = ("an example", 1, 10)

and you can do everything above, except using the position-based names:

println(getResult._3)   // Prints 10

Upvotes: 6

paradigmatic
paradigmatic

Reputation: 40461

Tuples are OK but you can also consider case class:

case class Result( words: String, row: Int, col: Int ) 

It's immutable and has lots of useful features. I prefer case class over tuples, because fields have useful names. Learn about it here: http://www.codecommit.com/blog/scala/case-classes-are-cool

Upvotes: 1

Andrzej Doyle
Andrzej Doyle

Reputation: 103787

Multiple assigment can be performed in a syntactically simple fashion (which relies on the Tuple types' unapply methods), as follows:

val (foo, bar) = ("Hello", "world")

// The above is entirely equivalent to:
// val foo = "Hello"
// val bar = "world"
//
// or:
//
// val tmp = ("Hello", "world")
// val foo = tmp._1
// val bar = tmp._2

Hence you can simply change the latter example to:

var (words, row, col) =  if(someValue) {
  getResults(someValue)
} else {
  getResults(someOtherValue)
}

The if statement will return a (String, Int, Int) and the relevant components will be assigned to words, row and col in order.

If you were asking about how to annotate your method declaration with the return type, that's easy too:

def getResult(param: Int): (String, Int, Int) = {
   ...
}

As for whether it's better as a map - that depends entirely on the semantics of your method. Are you returning several values at once (a tuple) or are you returning an association between keys and values (a map)? Whichever one feels most natural and convenient is the one that you should use (at least in the absence of other concerns).

Upvotes: 21

Dan Simon
Dan Simon

Reputation: 13117

Tuples as return values are useful when you have a function that needs to return more than one value and where those values won't need to remain together as a structure (if they do need to stay together it would make more sense to define a simple case class).

To declare a function with a 3-tuple as the return type and return a 3-tuple, you would do :

def getResults(param: Int) : (String, Int, Int) = {
  ...
  (stringval, intval1, intval2)
}

This is actually syntactic sugar for:

def getResults(param: Int) : Tuple3[String, Int, Int] = {
  ...
  new Tuple3[String,Int,Int](stringval, intval1, intval2)
}

Scala has TupleN classes for N: 1 to 22

Upvotes: 5

Related Questions