5

Let's say I have this collection:

val a = Array(Array(1,2,3,4,5),Array(4,5),Array(5),Array(1,2,6,7,8))

Is there a way to define an extractor which would work in the following way:

a.foreach(e => {
   e match {
      case Array( ending with 5 ) => 
      case _ =>
   }
})

Sorry for the pseudocode, but I don't know how to express it. Is there a way to match something having 5 as the last element? What if I would want to match something having a 1 as the first element and a 5 as the last? Could this work for arrays of various lengths ( note that I specifically chose different lengths for my arrays in the example ).

Thanks!

Senthess
  • 17,020
  • 5
  • 23
  • 28
  • 3
    this is very closely related: http://stackoverflow.com/questions/6697645/scala-get-first-and-last-elements-of-list-using-pattern-matching – Kim Stebel Jul 15 '11 at 20:34
  • Try to avoid deconstructing arrays over extractors, they get looped/copied to Seq with O(n) complexity. – lisak Apr 27 '14 at 19:19

4 Answers4

11

Yes you can:

object EndsWith {
  def unapply[A]( xs: Array[A] ) = 
    if( xs.nonEmpty ) Some( xs.last ) else None
}

On your example:

val a = Array(Array(1,2,3,4,5),Array(4,5),Array(5),Array(1,2,6,7,8))

a foreach { 
  case e @ EndsWith(5) => println( e.mkString("(",",",")" ) )
  case _ =>
}

It prints as expected (1,2,3,4,5), (4,5) and (5)

With the same approach, you could write an extractor StartWith and then add a method to combine them in a new extractor matching both conditions.

paradigmatic
  • 40,153
  • 18
  • 88
  • 147
9
a.foreach(e => {
   e match {
      case a: Array[Int] if a.last == 5 => 
      case _ =>
   }
})

You can do something a little better for matching on the first elements:

a.foreach(e => {
   e match {
      case Array(1, _*) => 
      case _ => 
   }
})

Unfortunately the @_* thing has to be the last item in the list of array arguments. But you can make the matching before that as complex as you want.

scala> val Array(1, x @_*) = Array(1,2,3,4,5)
x: Seq[Int] = Vector(2, 3, 4, 5)

scala> val Array(1, b, 3, x @_*) = Array(1,2,3,4,5)
b: Int = 2
x: Seq[Int] = Vector(4, 5)
dhg
  • 52,383
  • 8
  • 123
  • 144
  • 1
    What's the point of binding `_*` to `x`? Even more so, to `_`? – Jean-Philippe Pellet Jul 15 '11 at 20:27
  • @Jean-Philippe Pellet: The poster didn't specify whether the array contents were actually needed. If they're needed, then binding them to a variable allows you to use them. – dhg Jul 15 '11 at 20:35
  • @Jean-Philippe Pellet: Good call on the `_` though; I didn't realize you could do `_*` by itself. Thanks. – dhg Jul 15 '11 at 20:41
4

The case syntax supports ifs, so this would work:

a foreach {
  case a: Array[Int] if a.last == 5 =>
  case _ =>
}
kiritsuku
  • 52,967
  • 18
  • 114
  • 136
sblundy
  • 60,628
  • 22
  • 121
  • 123
1
a.foreach (ar => ar.last match {                    
  case 5 => println ("-> 5] " + ar.mkString ("~"))
  case _ => println ("   ?] " + ar.mkString (":")) }) 

Why don't you match directly for the last element?

-> 5] 1~2~3~4~5
-> 5] 4~5
-> 5] 5
   ?] 1:2:6:7:8
user unknown
  • 35,537
  • 11
  • 75
  • 121