15

I am doing a pattern matching on a list. Is there anyway I can access the first and last element of the list to compare?

I want to do something like..

case List(x, _*, y) if(x == y) => true

or

case x :: _* :: y =>

or something similar... where x and y are first and last elements of the list..

How can I do that.. any Ideas?

Teja Kantamneni
  • 17,402
  • 12
  • 56
  • 86
  • From scala 2.10 you can just use for last def last[T](xs : Seq[T]) = { xs match { case _ :+ x => x }}, see https://issues.scala-lang.org/browse/SI-2575 – Andrzej Jozwik Sep 11 '13 at 08:05

4 Answers4

26

Use the standard :+ and +: extractors from the scala.collection package


ORIGINAL ANSWER

Define a custom extractor object.

object :+ {
  def unapply[A](l: List[A]): Option[(List[A], A)] = {
    if(l.isEmpty)
      None
    else 
      Some(l.init, l.last)
  }
}

Can be used as:

val first :: (l :+ last) = List(3, 89, 11, 29, 90)
println(first + " " + l + " " + last) // prints 3 List(89, 11, 29) 90

(For your case: case x :: (_ :+ y) if(x == y) => true)

Andrey
  • 8,882
  • 10
  • 58
  • 82
missingfaktor
  • 90,905
  • 62
  • 285
  • 365
  • Neat solution. What rule/trick in Scala allows `(l :+ last)` to work the same as `(:+(l, last))`? Didn't realize this was possible. – overthink Jul 14 '11 at 19:33
  • 2
    Answering my own question -- seems to be answered here: http://stackoverflow.com/questions/1059145/how-is-this-case-class-match-pattern-working/1059161#1059161 with more details here: http://www.artima.com/pins1ed/working-with-lists.html#16.5 – overthink Jul 14 '11 at 20:04
18

In case you missed the obvious:

case list @ (head :: tail) if head == list.last => true

The head::tail part is there so you don’t match on the empty list.

Debilski
  • 66,976
  • 12
  • 110
  • 133
  • Can you please elaborate meaning of ```@``` keyword and its appliance on ```(head :: tail)``` expression? Thank you in advance. – NikolaS Apr 28 '22 at 08:59
11

simply:

case head +: _ :+ last => 

for example:

scala> val items = Seq("ham", "spam", "eggs")
items: Seq[String] = List(ham, spam, eggs)

scala> items match {
     |   case head +: _ :+ last => Some((head, last))
     |   case List(head) => Some((head, head))
     |   case _ => None
     | }
res0: Option[(String, String)] = Some((ham,eggs))
Thomas Grainger
  • 2,271
  • 27
  • 34
2

Lets understand the concept related to this question, there is a difference between '::', '+:' and ':+':

1st Operator:

'::' - It is right associative operator which works specially for lists

scala> val a :: b :: c = List(1,2,3,4)
a: Int = 1
b: Int = 2
c: List[Int] = List(3, 4)

2nd Operator:

'+:' - It is also right associative operator but it works on seq which is more general than just list.

scala> val a +: b +: c = List(1,2,3,4)
a: Int = 1
b: Int = 2
c: List[Int] = List(3, 4)

3rd Operator:

':+' - It is also left associative operator but it works on seq which is more general than just list

scala> val a :+ b :+ c = List(1,2,3,4)
a: List[Int] = List(1, 2)
b: Int = 3
c: Int = 4

The associativity of an operator is determined by the operator’s last character. Operators ending in a colon ‘:’ are right-associative. All other operators are left-associative.

A left-associative binary operation e1;op;e2 is interpreted as e1.op(e2)

If op is right-associative, the same operation is interpreted as { val x=e1; e2.op(x) }, where x is a fresh name.

Now comes answer for your question: So now if you need to get first and last element from the list, please use following code

scala> val firstElement +: b :+ lastElement = List(1,2,3,4)
firstElement: Int = 1
b: List[Int] = List(2, 3)
lastElement: Int = 4
amandeep1991
  • 1,344
  • 13
  • 17