最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

scala - How can I find the only matching element in a Seq[A] as an Option[A], throwing an exception if there is more than one ma

programmeradmin3浏览0评论

In some test code, I have a Seq[A] that I'm filtering down to items matching a given property. There should be at most one such element in the sequence. If it has more, it means I set up something wrong in the test.

val expected: Seq[MyType] = values.filter(_.something)

I would like this to be an Option[MyType], throwing an exception indicating a test setup failure if there are two or more elements in the filtered sequence.

The following works, but it feels like there's likely to be a simpler, more idiomatic way in Scala.

val expected: Option[MyType] = values.filter(_.something) match {
  case v if v.size > 1 => throw new AssertionError("Error in test setup")
  case v => v.headOption
}

How can I cleanly filter a Seq[A] to an Option[A], throwing an exception if there is more than one element in the sequence?

In some test code, I have a Seq[A] that I'm filtering down to items matching a given property. There should be at most one such element in the sequence. If it has more, it means I set up something wrong in the test.

val expected: Seq[MyType] = values.filter(_.something)

I would like this to be an Option[MyType], throwing an exception indicating a test setup failure if there are two or more elements in the filtered sequence.

The following works, but it feels like there's likely to be a simpler, more idiomatic way in Scala.

val expected: Option[MyType] = values.filter(_.something) match {
  case v if v.size > 1 => throw new AssertionError("Error in test setup")
  case v => v.headOption
}

How can I cleanly filter a Seq[A] to an Option[A], throwing an exception if there is more than one element in the sequence?

Share Improve this question asked Mar 19 at 20:40 M. JustinM. Justin 21.9k11 gold badges130 silver badges164 bronze badges 6
  • 1 Why not just assert(expected.size <= 1)? – Tim Commented Mar 19 at 23:36
  • You may use a foldLeft where the accumulator is an Option[A] with an initial value of None, when something satisfies the predicate, then you check the accumulator, if it is a None you give it a Some(a) but if it was already a Some then you throw. – Luis Miguel Mejía Suárez Commented Mar 19 at 23:37
  • 1 Keep your tests readable: an assert or similar will be the most straightforward thing for a future reader of the code. – Gaël J Commented Mar 20 at 6:23
  • @Tim That works too (though it requires a separate statement between filtering and converting to the option). I was mostly wondering if there was a terse, idiomatic way of converting a Seq that should have 0 or 1 elements to an Option. – M. Justin Commented Mar 20 at 17:24
  • 1 @M.Justin well the idea was mostly to avoid the filter, so more like: val expected: Option[MyType] = values.foldLeft(Option.empty[MyType])((a,b) => if (b.something && a.isEmpty) Some(b) else throw new AssertionError("Error in test setup")) – Luis Miguel Mejía Suárez Commented Mar 20 at 17:37
 |  Show 1 more comment

3 Answers 3

Reset to default 2

I think this is reasonably clear

val found = values.filter(<predicate>)
assert(found.size <= 1)
val expected: Option[MyType] = found.headOption

This is less efficient but one line shorter

assert(values.count(<predicate>) < 2)
val expected = values.find(<predicate>)

The reduceOption method can be used, throwing an exception if the reduction operator is ever called (which will happen only when there are two or more elements).

val expected: Option[MyType] = values
        .filter(_.something)
        .reduceOption((_, _) => throw new AssertionError("Error in test setup"))

Since it is a reasonable solution (and so it's not just hiding in the question itself), there's the option of using match as suggested in the question:

val expected: Option[MyType] = values.view.filter(_.something) match {
  case v if v.sizeCompare(1) > 0 => throw new AssertionError("Error in test setup")
  case v => v.headOption
}

This is reasonably clear and not terribly verbose.

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论