Scala pattern matching: How to match on an element inside a list?

23,894

Solution 1

You could implement this using a function like

def onContains[T](xs: Seq[String], actionMappings: (String, T)*): Option[T] = {
  actionMappings collectFirst {
    case (str, v) if xs contains str => v
  }
}

And use it like this:

val x = onContains(items,
  "foo" -> FOO,
  "bar" -> BAR
)

Solution 2

You can add if conditions to matches like this:

ls match {
  case x if x.contains("foo") => // FOO
  case x if x.contains("bar") => // BAR
  case _ => // ELSE
}

However, it is not the nicest way, as each if check needs to traverse the list, so this doesn't scale well. There are various different ways to deal with this problem, but we would need to know more about your intensions, as normally the runtime semantics would differ from your code (for example, you could recursively traverse the list looking for either "foo" or "bar", but that would assume you only have either one in the list).

Solution 3

As Frank's answer says, it is possible, but expensive if you would do it the dirty way.

It depends on what you want to do. Do you want to return the index of that "foo" or "bar" (for example)? Then you would do something like this:

def indexOf[T]: (List[T], T) => Int = (ls, x) => ls match {
    case Nil => -1
    case e::es if( e.equals(x) ) => 0
    case e::es => val i = indexOf( es, x ); if( i < 0 ) i else i + 1
}

This code is not tested, but you get the idea.

Share:
23,894
Alex Vayda
Author by

Alex Vayda

Enthusiastic full-stack software engineer and active open-source contributor. Scala, Java, TypeScript, JavaScript Spring, Hibernate Angular Apache Spark MongoDb, ArangoDb Oracle, PostgreSQL ElasticSearch, Solr

Updated on July 09, 2022

Comments

  • Alex Vayda
    Alex Vayda almost 2 years

    Is it possible to rewrite the following code using Scala pattern matching?

    val ls: List[String] = ??? // some list of strings
    
    val res = if (ls.contains("foo")) FOO
         else if (ls.contains("bar")) BAR
         else SOMETHING_ELSE
    
  • Alex Vayda
    Alex Vayda over 11 years
    I thought did it that way but 'case' looks redundant as I have those 'if's anyway. I would want to write something like { case _ :: "foo" :: _ => ??? }
  • Alex Vayda
    Alex Vayda over 11 years
    Yes my cases should return a value (see my comment to the question). I like your approach, although it is much less concise than simple if-else construction. But I would need to have my constants (foo, bar, baz etc) to be listed twice in a code. I have a couple of dozens of them so I would avoid this.
  • Kevin Meredith
    Kevin Meredith almost 11 years
    thanks for answer. What does the * mean in actionMappings: (String, T)*?
  • Marius Danila
    Marius Danila almost 11 years
    It allows to define a method to have a variable number of arguments which can be accessed as a Seq collection. tutorialspoint.com/scala/functions_variable_arguments.htm