ChuckCottrill
ChuckCottrill

Reputation: 4444

How can one iterate over list of tuples/arrays, using contents of inner list to determine behavior?

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

Answers (3)

WillD
WillD

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

Dhirendra
Dhirendra

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

rogue-one
rogue-one

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

Related Questions