Reputation: 4444
Suppose you have a list of lists in Scala,
thing = List( List("a", "AAA", true)
,List("b", "BBB", true)
,List("c", "CCC", true)
,List("d", "DDD", false)
,List("e", "EEE", true) )
And you want to iterate through the list, and use the elements of the inner list for some additional work.
Python would be easy,
foreach x in thing:
if x[2]: func1( x[0], x[1] )
else: func2( x[0], x[1], something_else )
I think this can be roughly translated to scala as something like this,
thing.foreach { x =>
if( x.lift(2) ) func1( x.lift(0), x.lift(1) )
else func2( x.lift(0), x.lift(1), something_else )
}
Is the above idiomatic scala? What is the scala idiom for doing this?
Upvotes: 0
Views: 1305
Reputation: 875
I'd suggest using a List (or better yet, a Seq) of Tuples instead of a List of Lists. It's more idiomatic for Scala
val thing = Seq(("a", "AAA", true)
,("b", "BBB", true)
,("c", "CCC", true)
,("d", "DDD", false)
,("e", "EEE", true)
)
Then make your decision with case statments
thing.map{
_ match {
case (x, y, true) => func1(x,y)
case (x, y, false) => func2(x,y,something_else)
case _ => throw new UnsupportedOperationException("Ack!")
}
}
Using a case class instead of the Tuple would allow you to type the various values. This would obviate the last case statement, among other advantages. Scala is type safe. This is good.
case class Thing(x:String,y:String,z:Boolean)
val thing = Seq[Thing](Thing("a", "AAA", true)
,Thing("b", "BBB", true)
,Thing("c", "CCC", true)
,Thing("d", "DDD", false)
,Thing("e", "EEE", true)
)
The rest is the same (Goooooooo Scala Type Inference!)
one more thing...
If a case class seems overkill (you won't use it elsewhere, code is tiny and localized) you can also type Tuples ->
val thing = Seq[(String,String,Boolean)](("a", "AAA", true)
...
Upvotes: 2
Reputation: 309
Try This
val thing = List( List("a", "AAA", true)
,List("b", "BBB", true)
,List("c", "CCC", true)
,List("d", "DDD", false)
,List("e", "EEE", true) )
thing.map{ x => x match{case List(x,y,z) => if(z == true) func1( x,y) else func2( x, y, something_else ) }}
Upvotes: 0
Reputation: 11587
foreach
should be used only for side-effecting functions ie if func1
and func2
returns Unit or you are not bothered with the return value. use map
if you want to generate a new list from the existing list thing
.
Drop the x.lift(2)
and use just x(2)
. x.lift(2)
would return an Option. lift
is required if you think the inner list might not have the third element at all. for instance x(2)
has value true
x.lift(2)
would return Some(true)
and if x(2)
has no value (i.e. the list has only 2 elements) x.lift(2)
would return None
use of Pattern matching is recommended here instead of if-else clause since your inner list doesnt have homogenous types (String
and Boolean
) and hence it will upcasted to type Any
. so x(2)
returns type Any
and not Boolean
as one would expect. The Pattern matching will eliminate the need for any ugly type casting.
thing foreach { //use map if func1 and func2 are not side-effecting functions
x => x(2) match {
case true => func1(x.lift(0), x.lift(1))
case false => func2(x.lift(0), x.lift(1), something_else)
}
}
If the inner list will always have three elements of types String, String and Boolean respectively consider using a Tuple instead of List. so the thing would look like the below.
val thing = List(("a", "AAA", True),("b", "BBB", True),("c", "CCC", True),("d", "DDD", False),("e", "EEE", True))
thing foreach {
case (x,y,true) => func1(x, y)
case (x,y,false) => func2(x, y, something_else)
}
Upvotes: 1