5

Learning Scala and I keep wanting an equivalent to LINQ's Single() method. Example,

val collection: Seq[SomeType]
val (desiredItem, theOthers) = collection.partition(MyFunc)

desiredItem.single.doSomething
         // ^^^^^^

I could use desiredItem.head but what if MyFunc actually matched several? I want the assurance that there's only one.

Edit #2 The duplicate question says 'no there isn't but here's how to build it'. So I am thinking if this was a common need it would be in the base API. Do properly written Scala programs need this?

Iain
  • 1,797
  • 1
  • 20
  • 38
  • Possible duplicate of [Chart of IEnumerable LINQ equivalents in Scala?](http://stackoverflow.com/questions/8104846/chart-of-ienumerable-linq-equivalents-in-scala) – Maxim Nov 24 '15 at 00:23
  • Thanks @Maxim. It says there is no builtin equivalent and roll your own. In which case my question: is it unidiomatic to need such a thing? – Iain Nov 24 '15 at 00:26

3 Answers3

3

I'd use something more verbose instead of single:

 (desiredItem match {
   case Seq(single) => single
   case _ => throw IllegalStateException("Not a single element!")
 }).doSomething

Its advantage over single is that it allows you to explicitly control the behavior in exceptional case (trow an exception, return fallback value).

Alternatively you can use destructuring assignment:

val Seq(single) = desiredItem
single.doSomething

In this case you'll get MatchError if desiredItem doesn't contain exactly one element.

UPD: I looked again at your code. Destructuring assignment is the way to go for you:

val collection: Seq[SomeType]
val (Seq(desiredItem), theOthers) = collection.partition(MyFunc)

desiredItem.doSomething
Aivean
  • 10,692
  • 25
  • 39
1

There's no prebuilt method in the API to do that. You can create your own method to do something similar though.

scala> def single[A](xs: List[A]) = xs match{ 
 | case List() => None
 | case x::Nil => Some(x)
 | case x::xs => throw new Exception("More than one element")
     | }
single: [A](xs: Seq[A])Option[A]

scala> single(List(1,2,3))
java.lang.Exception: More than one element
  at .single(<console>:11)
  ... 33 elided

scala> single(List(1))
res13: Any = Some(1)

scala> single(List())
res14: Any = None
Brian
  • 20,195
  • 6
  • 34
  • 55
  • Thanks. Do you usually define this method in your own programs? (I was wondering if it was intentionally left out of base API and I shouldn't need it) – Iain Nov 24 '15 at 00:35
  • 1
    I personally don't but you could if it comes up enough. I'll guess it was left out of the API because you can build it out of the functions provided. C# provides it in the API as a feature. – Brian Nov 24 '15 at 00:42
  • 1
    This is buggy; your method accepts all `Seq`s, but only actually works on `List`s. – Seth Tisue Nov 24 '15 at 02:45
1

Like others indicated, there is no library implementation of what you seek. But it's easy to implement your own using a Pimp My Library approach. For example you can do the following.

object Main extends App {
    object PML {
      implicit class TraversableOps[T](val collection: TraversableOnce[T]) {
        def single: Option[T] = collection.toList match {
          case List(x) => Some(x)
          case _ => None
        }
      }
    }

    import PML._
    val collection: Seq[Int] = Seq(1, 2)
    val (desiredItem, theOthers) = collection.partition(_ < 2)
    println(desiredItem.single)  // Some(1)
    println(collection.single)   // None
    println(List.empty.single)   // None
}
moem
  • 556
  • 3
  • 10